siat 3.10.132__py3-none-any.whl → 3.11.1__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.
Files changed (221) hide show
  1. siat/__init__.py +0 -0
  2. siat/allin.py +8 -0
  3. siat/assets_liquidity.py +0 -0
  4. siat/beta_adjustment.py +0 -0
  5. siat/beta_adjustment_china.py +0 -0
  6. siat/blockchain.py +0 -0
  7. siat/bond.py +0 -0
  8. siat/bond_base.py +0 -0
  9. siat/bond_china.py +0 -0
  10. siat/bond_zh_sina.py +0 -0
  11. siat/capm_beta.py +0 -0
  12. siat/capm_beta2.py +4 -4
  13. siat/common.py +9 -6
  14. siat/compare_cross.py +0 -0
  15. siat/copyrights.py +0 -0
  16. siat/cryptocurrency.py +0 -0
  17. siat/economy.py +0 -0
  18. siat/economy2.py +0 -0
  19. siat/esg.py +0 -0
  20. siat/event_study.py +0 -0
  21. siat/exchange_bond_china.pickle +0 -0
  22. siat/fama_french.py +0 -0
  23. siat/fin_stmt2_yahoo.py +0 -0
  24. siat/financial_base.py +0 -0
  25. siat/financial_statements.py +0 -0
  26. siat/financials.py +0 -0
  27. siat/financials2.py +0 -0
  28. siat/financials_china.py +0 -0
  29. siat/financials_china2.py +0 -0
  30. siat/fund.py +0 -0
  31. siat/fund_china.pickle +0 -0
  32. siat/fund_china.py +0 -0
  33. siat/future_china.py +0 -0
  34. siat/google_authenticator.py +0 -0
  35. siat/grafix.py +55 -4
  36. siat/holding_risk.py +0 -0
  37. siat/luchy_draw.py +0 -0
  38. siat/market_china.py +0 -0
  39. siat/markowitz.py +0 -0
  40. siat/markowitz2.py +1 -0
  41. siat/markowitz2_20250704.py +0 -0
  42. siat/markowitz2_20250705.py +0 -0
  43. siat/markowitz_simple.py +0 -0
  44. siat/ml_cases.py +0 -0
  45. siat/ml_cases_example.py +0 -0
  46. siat/option_china.py +0 -0
  47. siat/option_pricing.py +0 -0
  48. siat/other_indexes.py +0 -0
  49. siat/risk_adjusted_return.py +0 -0
  50. siat/risk_adjusted_return2.py +8 -4
  51. siat/risk_evaluation.py +0 -0
  52. siat/risk_free_rate.py +0 -0
  53. siat/save2docx.py +345 -0
  54. siat/save2pdf.py +145 -0
  55. siat/sector_china.py +0 -0
  56. siat/security_price2.py +0 -0
  57. siat/security_prices.py +168 -6
  58. siat/security_trend.py +0 -0
  59. siat/security_trend2.py +2 -2
  60. siat/stock.py +11 -1
  61. siat/stock_advice_linear.py +0 -0
  62. siat/stock_base.py +0 -0
  63. siat/stock_china.py +0 -0
  64. siat/stock_info.pickle +0 -0
  65. siat/stock_prices_kneighbors.py +0 -0
  66. siat/stock_prices_linear.py +0 -0
  67. siat/stock_profile.py +0 -0
  68. siat/stock_technical.py +0 -0
  69. siat/stooq.py +0 -0
  70. siat/transaction.py +0 -0
  71. siat/translate.py +0 -0
  72. siat/valuation.py +0 -0
  73. siat/valuation_china.py +0 -0
  74. siat/var_model_validation.py +0 -0
  75. siat/yf_name.py +0 -0
  76. {siat-3.10.132.dist-info/licenses → siat-3.11.1.dist-info}/LICENSE +0 -0
  77. {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/METADATA +234 -235
  78. siat-3.11.1.dist-info/RECORD +80 -0
  79. {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/WHEEL +1 -1
  80. {siat-3.10.132.dist-info → siat-3.11.1.dist-info}/top_level.txt +0 -1
  81. build/lib/build/lib/siat/__init__.py +0 -75
  82. build/lib/build/lib/siat/allin.py +0 -137
  83. build/lib/build/lib/siat/assets_liquidity.py +0 -915
  84. build/lib/build/lib/siat/beta_adjustment.py +0 -1058
  85. build/lib/build/lib/siat/beta_adjustment_china.py +0 -548
  86. build/lib/build/lib/siat/blockchain.py +0 -143
  87. build/lib/build/lib/siat/bond.py +0 -2900
  88. build/lib/build/lib/siat/bond_base.py +0 -992
  89. build/lib/build/lib/siat/bond_china.py +0 -100
  90. build/lib/build/lib/siat/bond_zh_sina.py +0 -143
  91. build/lib/build/lib/siat/capm_beta.py +0 -783
  92. build/lib/build/lib/siat/capm_beta2.py +0 -887
  93. build/lib/build/lib/siat/common.py +0 -5360
  94. build/lib/build/lib/siat/compare_cross.py +0 -642
  95. build/lib/build/lib/siat/copyrights.py +0 -18
  96. build/lib/build/lib/siat/cryptocurrency.py +0 -667
  97. build/lib/build/lib/siat/economy.py +0 -1471
  98. build/lib/build/lib/siat/economy2.py +0 -1853
  99. build/lib/build/lib/siat/esg.py +0 -536
  100. build/lib/build/lib/siat/event_study.py +0 -815
  101. build/lib/build/lib/siat/fama_french.py +0 -1521
  102. build/lib/build/lib/siat/fin_stmt2_yahoo.py +0 -982
  103. build/lib/build/lib/siat/financial_base.py +0 -1160
  104. build/lib/build/lib/siat/financial_statements.py +0 -598
  105. build/lib/build/lib/siat/financials.py +0 -2339
  106. build/lib/build/lib/siat/financials2.py +0 -1278
  107. build/lib/build/lib/siat/financials_china.py +0 -4433
  108. build/lib/build/lib/siat/financials_china2.py +0 -2212
  109. build/lib/build/lib/siat/fund.py +0 -629
  110. build/lib/build/lib/siat/fund_china.py +0 -3307
  111. build/lib/build/lib/siat/future_china.py +0 -551
  112. build/lib/build/lib/siat/google_authenticator.py +0 -47
  113. build/lib/build/lib/siat/grafix.py +0 -3636
  114. build/lib/build/lib/siat/holding_risk.py +0 -867
  115. build/lib/build/lib/siat/luchy_draw.py +0 -638
  116. build/lib/build/lib/siat/market_china.py +0 -1168
  117. build/lib/build/lib/siat/markowitz.py +0 -2363
  118. build/lib/build/lib/siat/markowitz2.py +0 -3150
  119. build/lib/build/lib/siat/markowitz2_20250704.py +0 -2969
  120. build/lib/build/lib/siat/markowitz2_20250705.py +0 -3158
  121. build/lib/build/lib/siat/markowitz_simple.py +0 -373
  122. build/lib/build/lib/siat/ml_cases.py +0 -2291
  123. build/lib/build/lib/siat/ml_cases_example.py +0 -60
  124. build/lib/build/lib/siat/option_china.py +0 -3069
  125. build/lib/build/lib/siat/option_pricing.py +0 -1925
  126. build/lib/build/lib/siat/other_indexes.py +0 -409
  127. build/lib/build/lib/siat/risk_adjusted_return.py +0 -1576
  128. build/lib/build/lib/siat/risk_adjusted_return2.py +0 -1900
  129. build/lib/build/lib/siat/risk_evaluation.py +0 -2218
  130. build/lib/build/lib/siat/risk_free_rate.py +0 -351
  131. build/lib/build/lib/siat/sector_china.py +0 -4140
  132. build/lib/build/lib/siat/security_price2.py +0 -727
  133. build/lib/build/lib/siat/security_prices.py +0 -3408
  134. build/lib/build/lib/siat/security_trend.py +0 -402
  135. build/lib/build/lib/siat/security_trend2.py +0 -646
  136. build/lib/build/lib/siat/stock.py +0 -4284
  137. build/lib/build/lib/siat/stock_advice_linear.py +0 -934
  138. build/lib/build/lib/siat/stock_base.py +0 -26
  139. build/lib/build/lib/siat/stock_china.py +0 -2095
  140. build/lib/build/lib/siat/stock_prices_kneighbors.py +0 -910
  141. build/lib/build/lib/siat/stock_prices_linear.py +0 -386
  142. build/lib/build/lib/siat/stock_profile.py +0 -707
  143. build/lib/build/lib/siat/stock_technical.py +0 -3305
  144. build/lib/build/lib/siat/stooq.py +0 -74
  145. build/lib/build/lib/siat/transaction.py +0 -347
  146. build/lib/build/lib/siat/translate.py +0 -5183
  147. build/lib/build/lib/siat/valuation.py +0 -1378
  148. build/lib/build/lib/siat/valuation_china.py +0 -2076
  149. build/lib/build/lib/siat/var_model_validation.py +0 -444
  150. build/lib/build/lib/siat/yf_name.py +0 -811
  151. build/lib/siat/__init__.py +0 -75
  152. build/lib/siat/allin.py +0 -137
  153. build/lib/siat/assets_liquidity.py +0 -915
  154. build/lib/siat/beta_adjustment.py +0 -1058
  155. build/lib/siat/beta_adjustment_china.py +0 -548
  156. build/lib/siat/blockchain.py +0 -143
  157. build/lib/siat/bond.py +0 -2900
  158. build/lib/siat/bond_base.py +0 -992
  159. build/lib/siat/bond_china.py +0 -100
  160. build/lib/siat/bond_zh_sina.py +0 -143
  161. build/lib/siat/capm_beta.py +0 -783
  162. build/lib/siat/capm_beta2.py +0 -887
  163. build/lib/siat/common.py +0 -5360
  164. build/lib/siat/compare_cross.py +0 -642
  165. build/lib/siat/copyrights.py +0 -18
  166. build/lib/siat/cryptocurrency.py +0 -667
  167. build/lib/siat/economy.py +0 -1471
  168. build/lib/siat/economy2.py +0 -1853
  169. build/lib/siat/esg.py +0 -536
  170. build/lib/siat/event_study.py +0 -815
  171. build/lib/siat/fama_french.py +0 -1521
  172. build/lib/siat/fin_stmt2_yahoo.py +0 -982
  173. build/lib/siat/financial_base.py +0 -1160
  174. build/lib/siat/financial_statements.py +0 -598
  175. build/lib/siat/financials.py +0 -2339
  176. build/lib/siat/financials2.py +0 -1278
  177. build/lib/siat/financials_china.py +0 -4433
  178. build/lib/siat/financials_china2.py +0 -2212
  179. build/lib/siat/fund.py +0 -629
  180. build/lib/siat/fund_china.py +0 -3307
  181. build/lib/siat/future_china.py +0 -551
  182. build/lib/siat/google_authenticator.py +0 -47
  183. build/lib/siat/grafix.py +0 -3636
  184. build/lib/siat/holding_risk.py +0 -867
  185. build/lib/siat/luchy_draw.py +0 -638
  186. build/lib/siat/market_china.py +0 -1168
  187. build/lib/siat/markowitz.py +0 -2363
  188. build/lib/siat/markowitz2.py +0 -3150
  189. build/lib/siat/markowitz2_20250704.py +0 -2969
  190. build/lib/siat/markowitz2_20250705.py +0 -3158
  191. build/lib/siat/markowitz_simple.py +0 -373
  192. build/lib/siat/ml_cases.py +0 -2291
  193. build/lib/siat/ml_cases_example.py +0 -60
  194. build/lib/siat/option_china.py +0 -3069
  195. build/lib/siat/option_pricing.py +0 -1925
  196. build/lib/siat/other_indexes.py +0 -409
  197. build/lib/siat/risk_adjusted_return.py +0 -1576
  198. build/lib/siat/risk_adjusted_return2.py +0 -1900
  199. build/lib/siat/risk_evaluation.py +0 -2218
  200. build/lib/siat/risk_free_rate.py +0 -351
  201. build/lib/siat/sector_china.py +0 -4140
  202. build/lib/siat/security_price2.py +0 -727
  203. build/lib/siat/security_prices.py +0 -3408
  204. build/lib/siat/security_trend.py +0 -402
  205. build/lib/siat/security_trend2.py +0 -646
  206. build/lib/siat/stock.py +0 -4284
  207. build/lib/siat/stock_advice_linear.py +0 -934
  208. build/lib/siat/stock_base.py +0 -26
  209. build/lib/siat/stock_china.py +0 -2095
  210. build/lib/siat/stock_prices_kneighbors.py +0 -910
  211. build/lib/siat/stock_prices_linear.py +0 -386
  212. build/lib/siat/stock_profile.py +0 -707
  213. build/lib/siat/stock_technical.py +0 -3305
  214. build/lib/siat/stooq.py +0 -74
  215. build/lib/siat/transaction.py +0 -347
  216. build/lib/siat/translate.py +0 -5183
  217. build/lib/siat/valuation.py +0 -1378
  218. build/lib/siat/valuation_china.py +0 -2076
  219. build/lib/siat/var_model_validation.py +0 -444
  220. build/lib/siat/yf_name.py +0 -811
  221. siat-3.10.132.dist-info/RECORD +0 -218
@@ -1,2900 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- 本模块功能:债券,应用层
4
- 所属工具包:证券投资分析工具SIAT
5
- SIAT:Security Investment Analysis Tool
6
- 创建日期:2020年1月8日
7
- 最新修订日期:2020年5月19日
8
- 作者:王德宏 (WANG Dehong, Peter)
9
- 作者单位:北京外国语大学国际商学院
10
- 版权所有:王德宏
11
- 用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
12
- 特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
13
- """
14
-
15
- #==============================================================================
16
- #关闭所有警告
17
- import warnings; warnings.filterwarnings('ignore')
18
- from siat.grafix import *
19
- from siat.common import *
20
- from siat.translate import *
21
- from siat.bond_base import *
22
-
23
- #==============================================================================
24
- import matplotlib.pyplot as plt
25
- #plt.rcParams['figure.figsize']=(12.8,7.2)
26
- plt.rcParams['figure.figsize']=(12.8,6.4)
27
- plt.rcParams['figure.dpi']=300
28
- plt.rcParams['font.size'] = 13
29
- plt.rcParams['xtick.labelsize']=11 #横轴字体大小
30
- plt.rcParams['ytick.labelsize']=11 #纵轴字体大小
31
-
32
- title_txt_size=16
33
- ylabel_txt_size=14
34
- xlabel_txt_size=14
35
- legend_txt_size=14
36
-
37
- #处理绘图汉字乱码问题
38
- import sys; czxt=sys.platform
39
- if czxt in ['win32','win64']:
40
- plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
41
- mpfrc={'font.family': 'SimHei'}
42
-
43
- if czxt in ['darwin']: #MacOSX
44
- plt.rcParams['font.family']= ['Heiti TC']
45
- mpfrc={'font.family': 'Heiti TC'}
46
-
47
- if czxt in ['linux']: #website Jupyter
48
- plt.rcParams['font.family']= ['Heiti TC']
49
- mpfrc={'font.family':'Heiti TC'}
50
-
51
- # 解决保存图像时'-'显示为方块的问题
52
- plt.rcParams['axes.unicode_minus'] = False
53
- #==============================================================================
54
- def interbank_bond_issue_monthly(df,fromdate='*DEFAULT',todate='*DEFAULT',type='ALL'):
55
- """
56
- 功能:获得银行间债券市场发行金额,按月累计
57
- 输入:债券发行记录明细df,开始日期fromdate,截止日期todate;
58
- 债券类型type,默认所有类型
59
- 类型:SCP 超短期融资券,CP 短期融资券(短融),PPN 定向工具(私募券),
60
- MTN 中期票据(中票),ABN 资产支持票据,PRN 项目收益票据,SMECN 中小集合票据
61
- PB指的就是熊猫债。熊猫债是指境外和多边金融机构等在华发行的人民币债券。
62
- DFI债务融资工具,PN/PPN定向工具(私募券)。
63
- """
64
- curfunc=sys._getframe().f_code.co_name #获取当前函数名
65
- #过滤日期
66
- import pandas as pd
67
- if fromdate.upper() != '*DEFAULT':
68
- #测试开始日期的合理性
69
- try:
70
- start=pd.to_datetime(fromdate)
71
- except:
72
- print(" #Error("+curfunc+"), invalid date:",fromdate)
73
- return None
74
- df=df.reset_index(drop = True)
75
- df=df.drop(df[df['releaseTime2']<start].index)
76
-
77
- if todate.upper() != '*DEFAULT':
78
- #测试结束日期的合理性
79
- try:
80
- end=pd.to_datetime(todate)
81
- except:
82
- print(" #Error(interbank_bond_issue_monthly), invalid:",todate)
83
- return None
84
- df=df.reset_index(drop = True)
85
- df=df.drop(df[df['releaseTime2']>end].index)
86
-
87
- #检查债券类型
88
- bondtype=type.upper()
89
- typelist=['PN','SCP','MTN','ABN','PB','CP','PRN','PB-MTN','DFI','ALL']
90
- if not (bondtype in typelist):
91
- print(" #Error(interbank_bond_issue_monthly), unsupported bond type:",type)
92
- print(" Supported bond types:",typelist)
93
- return None
94
-
95
- #过滤债券类型
96
- ibbid=df
97
- if bondtype != 'ALL':
98
- ibbid=df.drop(df[df['regPrdtType']!=bondtype].index)
99
- ibbid=ibbid.reset_index(drop = True)
100
-
101
- #统计每月债券发行量
102
- lway=lambda x: x[0:7]
103
- ibbid['Year_Month']=ibbid['releaseDate'].map(lway).astype('str')
104
- ibbid['issueAmount']=ibbid['firstIssueAmount'].astype('float64')
105
- import pandas as pd
106
- ibbim=pd.DataFrame(ibbid.groupby(by=['Year_Month'])['issueAmount'].sum())
107
- #升序排列
108
- ibbim.sort_values(by=['Year_Month'],ascending=[True],inplace=True)
109
-
110
- #绘图
111
- titletxt=texttranslate("中国债券市场月发行量")
112
- if bondtype != 'ALL':
113
- titletxt=titletxt+"("+bondtype+")"
114
- import datetime
115
- today = datetime.date.today().strftime("%Y-%m-%d")
116
- footnote=texttranslate("数据来源:中国银行间市场交易商协会(NAFMII),")+today
117
- plot_line(ibbim,'issueAmount',texttranslate("发行量"),texttranslate("金额(亿元)"), \
118
- titletxt,footnote,power=4)
119
-
120
- return ibbim
121
-
122
-
123
- if __name__=='__main__':
124
- fromdate='2010-1-1'
125
- todate='2019-12-31'
126
- ibbi=interbank_bond_issue_detail(fromdate,todate)
127
- save_to_excel(ibbi,"S:/siat","bond_issue_monthly_2012_2019.xlsx")
128
-
129
- import pandas as pd
130
- io=r"S:/siat/bond_issue_monthly_2012_2019.xlsx"
131
- ibbi=pd.read_excel(io)
132
- del ibbi['Unnamed: 0']
133
- df=ibbi
134
-
135
- fromdate='2018-1-1'; todate='2020-12-31'; type='SCP'
136
- ibbim=interbank_bond_issue_monthly(ibbi,fromdate,todate)
137
- ibbim=interbank_bond_issue_monthly(ibbi,fromdate,todate,type='SCP')
138
- ibbim=interbank_bond_issue_monthly(ibbi,fromdate,todate,type='CP')
139
- ibbim=interbank_bond_issue_monthly(ibbi,fromdate,todate,type='MTN')
140
- ibbim=interbank_bond_issue_monthly(ibbi,fromdate,todate,type='ABN')
141
- ibbim=interbank_bond_issue_monthly(ibbi,fromdate,todate,type='PN')
142
-
143
- #==============================================================================
144
- def interbank_bond_issue_yearly(df,type='ALL'):
145
- """
146
- 功能:获得银行间债券市场发行金额,按月累计
147
- 输入:债券发行记录明细df;
148
- 债券类型type,默认所有类型
149
- 类型:SCP 超短期融资券,CP 短期融资券(短融),PPN 定向工具(私募券),
150
- MTN 中期票据(中票),ABN 资产支持票据,PRN 项目收益票据,SMECN 中小集合票据
151
- PB指的就是熊猫债。熊猫债是指境外和多边金融机构等在华发行的人民币债券。
152
- DFI债务融资工具,PN/PPN定向工具(私募券)。
153
- """
154
-
155
- #检查债券类型
156
- bondtype=type.upper()
157
- typelist=['PN','SCP','MTN','ABN','PB','CP','PRN','PB-MTN','DFI','ALL']
158
- if not (bondtype in typelist):
159
- print("...Error(interbank_bond_issue_monthly), unsupported bond type:",type)
160
- print(" Supported bond types:",typelist)
161
- return None
162
-
163
- #过滤债券类型
164
- ibbid=df
165
- if bondtype != 'ALL':
166
- ibbid=df.drop(df[df['regPrdtType']!=bondtype].index)
167
- ibbid=ibbid.reset_index(drop = True)
168
-
169
- #统计每年债券发行量
170
- ibbid['issueAmount']=ibbid['firstIssueAmount'].astype('float64')
171
- import pandas as pd
172
- ibbim=pd.DataFrame(ibbid.groupby(by=['releaseYear'])['issueAmount'].sum())
173
- #升序排列
174
- ibbim.sort_values(by=['releaseYear'],ascending=[True],inplace=True)
175
-
176
- #绘图
177
- titletxt="中国债券市场年发行量"
178
- if bondtype != 'ALL':
179
- titletxt=titletxt+"("+bondtype+")"
180
- import datetime
181
- today = datetime.date.today().strftime("%Y-%m-%d")
182
- footnote=texttranslate("数据来源:中国银行间市场交易商协会(NAFMII),")+today
183
- plot_line(ibbim,'issueAmount',texttranslate("发行量"),texttranslate("金额(亿元)"), \
184
- titletxt,footnote,power=4)
185
-
186
- return ibbim
187
-
188
- if __name__=='__main__':
189
- fromdate='2010-1-1'
190
- todate='2019-12-31'
191
- ibbim=interbank_bond_issue_detail(fromdate,todate)
192
- save_to_excel(ibbim,"S:/siat","bond_issue_monthly_2012_2019.xlsx")
193
-
194
- import pandas as pd
195
- io=r"S:/siat/bond_issue_monthly_2012_2019.xlsx"
196
- ibbi=pd.read_excel(io)
197
- del ibbi['Unnamed: 0']
198
-
199
- ibbiy=interbank_bond_issue_yearly(ibbi,type='SCP')
200
- ibbiy=interbank_bond_issue_yearly(ibbi,type='CP')
201
-
202
-
203
- #==============================================================================
204
- def interbank_bond_quote(rank=10,option='1'):
205
- """
206
- 功能:获得银行间债券市场现券报价
207
- 输入:从头开始显示的个数num;选项option:默认1按照收益率从高到低排列,
208
- 2按照发行时间从早到晚排列,3按照报价机构排列。其他选项按照默认排列。
209
- """
210
- num=rank
211
-
212
- #抓取银行间市场债券报价
213
- import akshare as ak
214
- try:
215
- df=ak.bond_spot_quote()
216
- except:
217
- print(" #Error(interbank_bond_quote): failed to capture bond quotes")
218
- return None
219
-
220
- #其他选项均作为默认选项
221
- if not option in ['1','2','3','4']: option='1'
222
- if option=='1':
223
- df.sort_values(by=['卖出收益率'],ascending=[False],inplace=True)
224
- optiontxt=texttranslate("收益率从高到低")
225
- if option=='2':
226
- df.sort_values(by=['债券简称'],ascending=[True],inplace=True)
227
- optiontxt=texttranslate("发行时间从早到晚")
228
- if option=='3':
229
- df.sort_values(by=['债券简称'],ascending=[False],inplace=True)
230
- optiontxt=texttranslate("发行时间从晚到早")
231
- if option=='4':
232
- df.sort_values(by=['报价机构'],ascending=[True],inplace=True)
233
- optiontxt=texttranslate("报价机构排序")
234
- #重新索引
235
- df.reset_index(drop=True,inplace=True)
236
- """
237
- print("\n"+texttranslate("中国银行间市场债券现券即时报价")+"("+optiontxt+texttranslate(",前")+str(num)+texttranslate("名)"))
238
- import pandas as pd
239
- pd.set_option('display.unicode.ambiguous_as_wide', True)
240
- pd.set_option('display.unicode.east_asian_width', True)
241
- pd.set_option('display.width', 180) # 设置打印宽度(**重要**)
242
- print(df.head(num).to_string(index=False))
243
-
244
- import datetime
245
- today = datetime.date.today().strftime("%Y-%m-%d")
246
- footnote="\n"+texttranslate("数据来源:中国银行间市场交易商协会(NAFMII),")+today
247
- print(footnote)
248
- """
249
- titletxt=texttranslate("中国银行间市场债券现券即时报价")+"("+optiontxt+texttranslate(",前")+str(num)+texttranslate("名)")
250
- import datetime
251
- todaydt = datetime.date.today().strftime("%Y-%m-%d")
252
- footnote="\n"+texttranslate("数据来源:中国银行间市场交易商协会(NAFMII),")+str(todaydt)
253
- df_display_CSS(df.head(num),titletxt=titletxt,footnote=footnote,facecolor='papayawhip',decimals=2, \
254
- first_col_align='left',second_col_align='left', \
255
- last_col_align='center',other_col_align='center')
256
-
257
- return df
258
-
259
- if __name__=='__main__':
260
- num=10
261
- option='1'
262
- ibbq=interbank_bond_quote(num,option)
263
- option='2'
264
- ibbq=interbank_bond_quote(num,option)
265
- option='6'
266
- ibbq=interbank_bond_quote(num,option)
267
-
268
- #==============================================================================
269
- if __name__=='__main__':
270
- btdf=interbank_bond_summary()
271
-
272
-
273
- def interbank_bond_summary():
274
- """
275
- 功能:获得银行间债券市场现券种类统计
276
- """
277
- #抓取银行间市场债券报价,需要akshare-1.4.47版及以后
278
- import akshare as ak
279
- df=ak.bond_spot_deal()
280
-
281
- btypelist=['PPN','CD','CP','SCP','MTN','GN','ABN','NPB', \
282
- '永续债','小微债','国债','二级','专项债','金融债', \
283
- '国开','农发','进出','绿色债','城投债']
284
- #df['类别']=''
285
- for t in btypelist:
286
- df[t]=df['债券简称'].apply(lambda x: 1 if t in x else 0)
287
-
288
- # 消除CP与SCP的重复
289
- df['CP']=df.apply(lambda x: 0 if x['SCP']==1 else x['CP'],axis=1)
290
-
291
- import pandas as pd
292
- btdf=pd.DataFrame(columns=['债券类别','数量','交易量'])
293
- for t in btypelist:
294
- tnum=df[t].sum()
295
-
296
- dftmp=df[df[t]==1]
297
- tamt=round(dftmp['交易量'].sum(),2)
298
-
299
- s=pd.Series({'债券类别':t,'数量':tnum,'交易量':tamt})
300
- try:
301
- btdf=btdf.append(s,ignore_index=True)
302
- except:
303
- btdf=btdf._append(s,ignore_index=True)
304
-
305
- # 其他类别
306
- dfnum=len(df)
307
- btnum=btdf['数量'].sum()
308
-
309
- df['其他类别']=df.apply(lambda x: x[btypelist].sum(),axis=1)
310
- dfqt=df[df['其他类别']==0]
311
- qtnum=len(dfqt)
312
- qtamt=round(dfqt['交易量'].sum(),2)
313
- s=pd.Series({'债券类别':'其他类别','数量':qtnum,'交易量':qtamt})
314
- try:
315
- btdf=btdf.append(s,ignore_index=True)
316
- except:
317
- btdf=btdf._append(s,ignore_index=True)
318
-
319
- btdf_num=btdf['数量'].sum()
320
- btdf_amt=btdf['交易量'].sum()
321
- # 交易量排名
322
- btdf.sort_values(by='交易量',ascending=False,inplace=True)
323
- btdf.reset_index(drop=True,inplace=True)
324
- btdf['交易量排名']=btdf.index + 1
325
- btdf['交易量占比(%)']=btdf['交易量'].apply(lambda x: round(x/btdf_amt*100,2))
326
-
327
- # 数量排名
328
- btdf.sort_values(by='数量',ascending=False,inplace=True)
329
- btdf.reset_index(drop=True,inplace=True)
330
- btdf['数量排名']=btdf.index + 1
331
- btdf['数量占比(%)']=btdf['数量'].apply(lambda x: round(x/btdf_num*100,2))
332
-
333
- # 整理字段的排列
334
- btcols=['债券类别', '数量', '数量排名', '数量占比(%)', '交易量', '交易量排名', '交易量占比(%)']
335
- btdf1=btdf[btcols]
336
-
337
- btdf2=btdf1.set_index('债券类别')
338
- btdf3=btdf2.T
339
- btcols2=list(btdf3)
340
- btcols2.remove('其他类别')
341
- btcols3=btcols2+['其他类别']
342
- btdf4=btdf3[btcols3]
343
- btdf5=btdf4.T
344
- btdf5.reset_index(inplace=True)
345
-
346
- btdf5.rename(columns={'交易量':'交易量(亿元)'})
347
-
348
- import numpy as np
349
- btdf5.replace(np.nan,'--',inplace=True)
350
- btdf5.replace(0,'--',inplace=True)
351
-
352
- print("\n=== 全国银行间债券市场现券种类与交易概况快照:当前共有"+str(dfnum)+"只可交易债券\n")
353
- alignlist=['left']+['center']*(len(btcols)-1)
354
- print(btdf5.to_markdown(index=False,tablefmt='plain',colalign=alignlist))
355
-
356
- import datetime as dt
357
- nowstr0=str(dt.datetime.now())
358
- nowstr=nowstr0[:19]
359
- print("\n*** 数据来源:全国银行间同业拆借中心,统计时间,",nowstr)
360
-
361
-
362
- print("\n*** 注释:")
363
- print(" ABN: 资产支持票据,由非金融企业发行,以标的资产产生的现金流作为还款支持;")
364
- print(" CD : 存款证书,由银行发行的可转让大额定期存款凭证;")
365
- print(" CP : 短期融资券,由企业发行的约定在一年期限内还本付息的融资债券;")
366
- print(" GN : 碳中和债券,募集资金专项用于具有碳减排效益的绿色项目;")
367
- print(" PPN: 定向债务融资工具,由非金融企业面向特定投资者发行,且只能在特定投资者之间流通;")
368
- print(" MTN: 中期票据,一种公司债务融资工具,具有若干到期期限供投资者选择,最长10年;")
369
- print(" NPB: 非公开项目收益债券,以标的项目产生的现金流作为还款支持;")
370
- print(" SCP: 超短期融债券,由非金融企业发行的期限在270天以内的融资债券;")
371
-
372
- print(" 二级: 一种中低风险的债券,可投资于二级股票市场(一般低于20%);")
373
- print(" 国开: 指政策性银行国家开发银行所发行的债券,信用风险较低;")
374
- print(" 农发: 指政策性银行中国农业发展银行所发行的债券,信用风险较低;")
375
- print(" 进出: 指政策性银行中国进出口银行所发行的债券,信用风险较低;")
376
- print(" 永续债: 指没有到期期限或到期期限非常长的债券,也称可续期公司债;")
377
- print(" 绿色债: 募集资金专门用于资助符合规定条件的绿色项目或为这些项目进行再融资的债券工具;")
378
- print(" 城投债: 由地方政府投融资平台发行,募集资金专门用于城市基础设施建设;")
379
- print(" 专项债: 由地方政府投融资平台发行,募集资金专门用于某个专项工程建设;")
380
- print(" 金融债: 由金融机构发行,募集资金用于解决资金来源不足和期限错配问题。")
381
-
382
- print(" 净价交易: 即按债券本金的市场价值报价和成交,不含债券附带的应计利息;")
383
- print(" 债券全价: 即债券交易成交后的结算价格,包括债券净价和附带的应计利息;")
384
- print(" 债券现券交易: 即债券的二级市场交易,成交后双方须在当日/次日办理券款交割;")
385
- print(" 债券收益率: 指当期收益率,债券年利息/当前市场价格;零息债券按发行价折算年利息")
386
- print(" 交易量信息: 单位为亿元人民币,一般需要在市场闭市一段时间后才有当日/前日统计数据。")
387
-
388
- return btdf5
389
-
390
-
391
- #==============================================================================
392
- if __name__=='__main__':
393
- num=10
394
- option='1'
395
-
396
- def interbank_bond_deal(rank=10,option='1'):
397
- """
398
- 功能:获得银行间债券市场现券成交行情
399
- 输入:从头开始显示的个数num;选项option:默认1按照收益率从高到低排列,
400
- 2按照发行时间从早到晚排列,3按照发行时间从晚到早排列,4按照涨跌幅从高到低,
401
- 5按照涨跌幅从低到高。
402
- 其他选项按照默认排列。
403
- """
404
- num=rank
405
-
406
- #抓取银行间市场债券报价,需要akshare-1.4.47版及以后
407
- import akshare as ak
408
- df=ak.bond_spot_deal()
409
-
410
- #丢弃某些列中有缺失值的行
411
- df.dropna(axis=0,subset=["加权收益率","涨跌","成交净价"],inplace=True)
412
-
413
- df['最新收益率']=df['最新收益率'].astype('float')
414
- df['加权收益率']=df['加权收益率'].astype('float')
415
- df['涨跌']=df['涨跌'].astype('float')
416
- df['成交净价']=df['成交净价'].astype('float')
417
- df['交易量']=df['交易量'].astype('float')
418
-
419
- df['最新收益率(%)']=df['最新收益率'].apply(lambda x: round(x,2))
420
- df['加权收益率%']=df['加权收益率'].apply(lambda x: round(x,2))
421
- df['涨跌(bp)']=df['涨跌'].apply(lambda x: round(x,2))
422
- df['成交净价(元)']=df['成交净价'].apply(lambda x: round(x,2))
423
- df['交易量(亿元)']=df['交易量'].apply(lambda x: round(x,2))
424
-
425
- """
426
- 成交净价 float64 注意单位: 元
427
- 最新收益率 float64 注意单位: %
428
- 涨跌 float64 注意单位: BP
429
- 加权收益率 float64 注意单位: %
430
- 交易量 float64 注意单位: 亿
431
- """
432
-
433
- #其他选项均作为默认选项
434
- lang=check_language()
435
-
436
- if not option in ['1','2','3','4','5','6','7','8']: option='1'
437
- if option=='1':
438
- df.sort_values(by=['最新收益率(%)'],ascending=[False],inplace=True)
439
- if lang == 'Chinese':
440
- optiontxt=texttranslate("收益率从高到低")
441
- else:
442
- optiontxt=texttranslate("Yield High to Low")
443
- collist=['债券简称', '最新收益率(%)', '成交净价(元)', '涨跌(bp)', '交易量(亿元)']
444
-
445
- if option=='2':
446
- df.sort_values(by=['债券简称'],ascending=[True],inplace=True)
447
- if lang == 'Chinese':
448
- optiontxt=texttranslate("发行时间从早到晚")
449
- else:
450
- optiontxt=texttranslate("Issued Early to Late")
451
- collist=['债券简称', '成交净价(元)', '涨跌(bp)', '交易量(亿元)', '最新收益率(%)']
452
-
453
- if option=='3':
454
- df.sort_values(by=['债券简称'],ascending=[False],inplace=True)
455
- if lang == 'Chinese':
456
- optiontxt=texttranslate("发行时间从晚到早")
457
- else:
458
- optiontxt=texttranslate("Issued Late to Early")
459
- collist=['债券简称', '成交净价(元)', '涨跌(bp)', '交易量(亿元)', '最新收益率(%)']
460
-
461
- if option=='4':
462
- df.sort_values(by=['涨跌(bp)'],ascending=[False],inplace=True)
463
- if lang == 'Chinese':
464
- optiontxt=texttranslate("涨跌幅从高到低")
465
- else:
466
- optiontxt=texttranslate("Change High to Low")
467
- collist=['债券简称', '涨跌(bp)', '成交净价(元)', '交易量(亿元)', '最新收益率(%)']
468
-
469
- if option=='5':
470
- df.sort_values(by=['涨跌(bp)'],ascending=[True],inplace=True)
471
- if lang == 'Chinese':
472
- optiontxt=texttranslate("涨跌幅从低到高")
473
- else:
474
- optiontxt=texttranslate("Change Low to High")
475
- collist=['债券简称', '涨跌(bp)', '成交净价(元)', '交易量(亿元)', '最新收益率(%)']
476
-
477
- if option=='6':
478
- df.sort_values(by=['成交净价(元)'],ascending=[False],inplace=True)
479
- if lang == 'Chinese':
480
- optiontxt=texttranslate("价格从高到低")
481
- else:
482
- optiontxt=texttranslate("Price High to Low")
483
- collist=['债券简称', '成交净价(元)', '涨跌(bp)', '交易量(亿元)', '最新收益率(%)']
484
-
485
- if option=='7':
486
- df.sort_values(by=['成交净价(元)'],ascending=[True],inplace=True)
487
- if lang == 'Chinese':
488
- optiontxt=texttranslate("价格从低到高")
489
- else:
490
- optiontxt=texttranslate("Price Low to High")
491
- collist=['债券简称', '成交净价(元)', '涨跌(bp)', '交易量(亿元)', '最新收益率(%)']
492
-
493
- if option=='8':
494
- df.sort_values(by=['交易量(亿元)'],ascending=[False],inplace=True)
495
- if lang == 'Chinese':
496
- optiontxt=texttranslate("交易量从高到低")
497
- else:
498
- optiontxt=texttranslate("Amount from High to Low")
499
- collist=['债券简称', '交易量(亿元)', '成交净价(元)', '涨跌(bp)', '最新收益率(%)']
500
-
501
- #删除不需要的字段和数据
502
- if num > 0:
503
- df1=df[collist].head(num)
504
- else:
505
- df1=df[collist].tail(-num)
506
-
507
- # 输出表格标题
508
- if lang == 'Chinese':
509
- if num > 0:
510
- #print("\n=== 全国银行间债券市场现券成交状况当前快照("+optiontxt+",前"+str(num)+"名)\n")
511
- titletxt="全国银行间债券市场现券成交状况当前快照("+optiontxt+",前"+str(num)+"名)"
512
- else:
513
- #print("\n=== 全国银行间债券市场现券成交状况当前快照("+optiontxt+",后"+str(-num)+"名)\n")
514
- titletxt="全国银行间债券市场现券成交状况当前快照("+optiontxt+",后"+str(-num)+"名)"
515
- else:
516
- if num > 0:
517
- #print("\n=== Interbank Bond Market: Deal Price ("+optiontxt+", Top "+str(num)+")\n")
518
- titletxt="Interbank Bond Market: Deal Price ("+optiontxt+", Top "+str(num)+")"
519
- else:
520
- #print("\n=== Interbank Bond Market: Deal Price ("+optiontxt+", Bottom "+str(-num)+")\n")
521
- titletxt="Interbank Bond Market: Deal Price ("+optiontxt+", Bottom "+str(-num)+")"
522
-
523
- """
524
- import pandas as pd
525
- pd.set_option('display.unicode.ambiguous_as_wide', True)
526
- pd.set_option('display.unicode.east_asian_width', True)
527
- pd.set_option('display.width', 180) # 设置打印宽度(**重要**)
528
- """
529
- if lang == 'English':
530
- df1.rename(columns={'债券简称':'Bond Name','成交净价(元)':'Net Price(RMB)', \
531
- '最新收益率(%)':'Latest Yield(%)','涨跌(bp)':'Change(bp)', \
532
- '交易量(亿元)':'Amount(100m RMB)'},inplace=True)
533
- # print(df1.head(num).to_string(index=False))
534
-
535
- # 打印df内容
536
- import numpy as np
537
- df1.replace(np.nan,'--',inplace=True)
538
- df1.replace(0,'--',inplace=True)
539
-
540
- df1.reset_index(drop=True,inplace=True)
541
- df1.index=df1.index + 1
542
-
543
- """
544
- numOfCol=len(list(df1))
545
- alignlist=['right','left']+['center']*(numOfCol - 1)
546
- print(df1.to_markdown(index=True,tablefmt='plain',colalign=alignlist))
547
- """
548
- import datetime as dt
549
- nowstr0=str(dt.datetime.now())
550
- nowstr=nowstr0[:19]
551
-
552
- if lang == 'Chinese':
553
- #print("\n*** 数据来源:全国银行间同业拆借中心,统计时间,",nowstr)
554
- footnote="数据来源:全国银行间同业拆借中心,统计时间, "+str(nowstr)
555
- else:
556
- #print("\n*** Data source:NAFMII. Delayed information,",nowstr)
557
- footnote="Data source:NAFMII. Delayed information, "+str(nowstr)
558
- df_display_CSS(df1,titletxt=titletxt,footnote=footnote,facecolor='papayawhip',decimals=2, \
559
- first_col_align='left',second_col_align='center', \
560
- last_col_align='center',other_col_align='center')
561
-
562
- return df
563
-
564
- if __name__=='__main__':
565
- num=10
566
- option='1'
567
- ibbd=interbank_bond_deal(num,option)
568
- option='2'
569
- ibbd=interbank_bond_deal(num,option)
570
- option='6'
571
- ibbd=interbank_bond_deal(num,option)
572
-
573
-
574
- #==============================================================================
575
- import os, sys
576
- class HiddenPrints:
577
- def __enter__(self):
578
- self._original_stdout = sys.stdout
579
- sys.stdout = open(os.devnull, 'w')
580
-
581
- def __exit__(self, exc_type, exc_val, exc_tb):
582
- sys.stdout.close()
583
- sys.stdout = self._original_stdout
584
- #==============================================================================
585
- if __name__=='__main__':
586
- num=10
587
- option='1'
588
-
589
- def exchange_bond_deal(rank=10,option='1'):
590
- """
591
- 功能:获得沪深债券市场现券成交行情
592
- 输入:从头开始显示的个数num;
593
- 选项option:默认1按照交易时间排列,
594
- 2按照发行时间从早到晚排列,3按照发行时间从晚到早排列,4按照涨跌幅从高到低,
595
- 5按照涨跌幅从低到高,6按照成交量从高到低排列,7按照成交量从低到高排列。
596
- 其他选项按照默认排列。
597
- """
598
- num=rank
599
-
600
- print(" Searching data, may take long time ...")
601
- #定义标准输出关闭类,在Spyder中无效
602
- import os, sys
603
- class HiddenPrints:
604
- def __enter__(self):
605
- self._original_stdout = sys.stdout
606
- sys.stdout = open(os.devnull, 'w')
607
- def __exit__(self, exc_type, exc_val, exc_tb):
608
- sys.stdout.close()
609
- sys.stdout = self._original_stdout
610
-
611
- import pandas as pd
612
- df=pd.DataFrame()
613
- #抓取银行间市场债券报价
614
- import akshare as ak
615
- with HiddenPrints():
616
- try:
617
- df=ak.bond_zh_hs_spot()
618
- except:
619
- pass
620
- if len(df)==0:
621
- print(" #Error(exchange_bond_deal),failed in getting info for now, try later.")
622
- return None
623
-
624
- #选取需要的字段
625
- df1=df[['代码','名称','最新价','涨跌幅','昨收','今开','最高', \
626
- '最低','买入','卖出','成交量']]
627
- #转换字符类型到数值类型
628
- df1['最新价']=df1['最新价'].astype("float64")
629
- df1['涨跌幅']=df1['涨跌幅'].astype("float64")
630
- df1['成交量']=df1['成交量'].astype("int")
631
-
632
- #其他选项均作为默认选项
633
- if not option in ['2','3','4','5','6','7']: option='2'
634
-
635
- lang=check_language()
636
- """
637
- if option=='1':
638
- df1.sort_values(by=['ticktime'],ascending=[True],inplace=True)
639
- optiontxt=texttranslate("按交易时间升序")
640
- """
641
- if option=='2':
642
- df1.sort_values(by=['名称'],ascending=[True],inplace=True)
643
- if lang=='Chinese':
644
- optiontxt=texttranslate("按债券名称升序")
645
- else:
646
- optiontxt=texttranslate("by Ascending Name")
647
-
648
- if option=='3':
649
- df1.sort_values(by=['名称'],ascending=[False],inplace=True)
650
- if lang=='Chinese':
651
- optiontxt=texttranslate("按债券名称降序")
652
- else:
653
- optiontxt=texttranslate("by Descending Name")
654
-
655
- if option=='4':
656
- df1.sort_values(by=['涨跌幅'],ascending=[False],inplace=True)
657
- if lang=='Chinese':
658
- optiontxt=texttranslate("按涨跌幅降序")
659
- else:
660
- optiontxt=texttranslate("Change High to Low")
661
-
662
- if option=='5':
663
- df1.sort_values(by=['涨跌幅'],ascending=[True],inplace=True)
664
- if lang=='Chinese':
665
- optiontxt=texttranslate("按涨跌幅升序")
666
- else:
667
- optiontxt=texttranslate("Change Low to High")
668
-
669
- if option=='6':
670
- df1.sort_values(by=['成交量'],ascending=[False],inplace=True)
671
- if lang=='Chinese':
672
- optiontxt=texttranslate("按成交量降序")
673
- else:
674
- optiontxt=texttranslate("Volume High to Low")
675
-
676
- if option=='7':
677
- df1.sort_values(by=['成交量'],ascending=[True],inplace=True)
678
- if lang=='Chinese':
679
- optiontxt=texttranslate("按成交量升序")
680
- else:
681
- optiontxt=texttranslate("Volume Low to High")
682
-
683
- #重新索引
684
- df1.reset_index(drop=True,inplace=True)
685
- """
686
- df2=df1.rename(columns={'ticktime':texttranslate('时间'),'symbol':texttranslate('债券代码'), \
687
- 'name':texttranslate('债券名称'),'trade':texttranslate('成交价'),'pricechange':texttranslate('涨跌(元)'), \
688
- 'open':texttranslate('开盘价'),'high':texttranslate('最高价'),'low':texttranslate('最低价'), \
689
- 'buy':texttranslate('买入价'),'sell':texttranslate('卖出价'),'volume':texttranslate('成交量')})
690
- """
691
- df1b=df1[['代码','名称','最新价','涨跌幅','昨收','成交量']]
692
- if lang=='Chinese':
693
- df2=df1b
694
- else:
695
- df2=df1b.rename(columns={'代码':'Code','名称':'Bond Name','最新价':'Latest Price', \
696
- '涨跌幅':'Change%','昨收':'Last Close','成交量':'Volume'})
697
-
698
- if lang=='Chinese':
699
- #print("\n"+texttranslate("交易所市场债券成交价(")+optiontxt+texttranslate(",前")+str(num)+texttranslate("名)"))
700
- titletxt=texttranslate("交易所市场债券成交价(")+optiontxt+texttranslate(",前")+str(num)+texttranslate("名)")
701
- else:
702
- #print("\nExchange Bond Market: Deal Price ("+optiontxt+", Top"+str(num)+")\n")
703
- titletxt="Exchange Bond Market: Deal Price ("+optiontxt+", Top"+str(num)+")"
704
-
705
- """
706
- pd.set_option('display.unicode.ambiguous_as_wide', True)
707
- pd.set_option('display.unicode.east_asian_width', True)
708
- pd.set_option('display.width', 200) # 设置打印宽度(**重要**)
709
- print(df2.head(num).to_string(index=False))
710
- """
711
- import datetime
712
- todaydt = datetime.date.today().strftime("%Y-%m-%d")
713
- if lang=='Chinese':
714
- #footnote="\n"+texttranslate("数据来源:新浪财经,")+today
715
- footnote=texttranslate("数据来源:新浪财经,")+str(todaydt)
716
- else:
717
- #footnote="\n"+texttranslate("Source: Sina Finance, ")+today
718
- footnote=texttranslate("Source: Sina Finance, ")+str(todaydt)
719
- #print(footnote)
720
-
721
- df_display_CSS(df2.head(num),titletxt=titletxt,footnote=footnote,facecolor='papayawhip',decimals=3, \
722
- first_col_align='left',second_col_align='left', \
723
- last_col_align='center',other_col_align='center')
724
-
725
- return df1
726
-
727
- if __name__=='__main__':
728
- num=10
729
- option='1'
730
- ebd=exchange_bond_deal(num,option)
731
- option='4'
732
- ebd=exchange_bond_deal(num,option)
733
- option='6'
734
- ebd=exchange_bond_deal(num,option)
735
-
736
- #==============================================================================
737
- if __name__=='__main__':
738
- symbol='sh019521'
739
- symbol='019521.SS'
740
- symbol='sz102229'
741
- symbol='149124.SZ'
742
- symbol='sh019319' #国债
743
-
744
- fromdate='2024-1-1'
745
- todate='2024-3-30'
746
- power=4
747
- graph=True
748
-
749
- prices=exchange_bond_price(symbol,fromdate,todate,power=power)
750
-
751
- #def exchange_bond_price(symbol,fromdate,todate,power=0,graph=True,data_crop=True):
752
- def exchange_bond_price(ticker,start,end='today',power=0,graph=True,data_crop=True):
753
- """
754
- 功能:获得沪深债券市场历史成交行情
755
- 输入:沪深债券代码symbol,起始日期fromdate,截止日期todate。
756
- 返回:历史价格df
757
- 输出:折线图
758
- """
759
- symbol=ticker
760
- fromdate,todate=start_end_preprocess(start,end)
761
-
762
- import pandas as pd
763
- import akshare as ak
764
- import datetime
765
-
766
- print(" Searching for bond",symbol,"\b, it may take great time, please wait ... ...")
767
-
768
- #检查日期期间的合理性
769
- result,start,end=check_period(fromdate, todate)
770
- if result is None: return None
771
-
772
- #变换代码格式
773
- symbol2=tickers_cvt2ak(symbol)
774
-
775
- #抓取历史行情
776
- try:
777
- df=ak.bond_zh_hs_daily(symbol=symbol2)
778
- trddate1=str(df.head(1)['date'].values[0])
779
- trddate2=str(df.tail(1)['date'].values[0])
780
- except:
781
- print(" #Error(exchange_bond_price), failed to get exchange bond prices of",symbol)
782
- print(" Currently support bonds traded in exchanges only")
783
- return None
784
-
785
- #是否过滤日期期间:债券有效时段较短,强制过滤时段可能形成空记录,影响其他函数判断
786
- if data_crop:
787
- df['datepd']=df['date'].apply(lambda x: pd.to_datetime(x))
788
- df.set_index('datepd',inplace=True)
789
- df2=df[(df.index >= start) & (df.index <= end)]
790
- df2.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close'},inplace=True)
791
-
792
- if len(df2) == 0:
793
- print(" #Warning(exchange_bond_price): no prices of",symbol,"between",fromdate,"and",todate)
794
- print(" Prices of",symbol,"exist between",trddate1,"and",trddate2)
795
- return df2
796
- else:
797
- df2=df
798
-
799
- #绘图
800
- if graph:
801
- todaydt = datetime.date.today().strftime("%Y-%m-%d")
802
- titletxt1=text_lang('沪深债券行情:','Exchange Bond Price Trend: ')
803
- titletxt=titletxt1+ticker_name(symbol,'bond')
804
- close_txt=text_lang('收盘价','Close')
805
- ylabel_txt=text_lang('价格','Price')
806
- footnote0=text_lang('数据来源:新浪,','Data source: sina, ')
807
- footnote=footnote0+todaydt
808
-
809
- plot_line(df2,'Close',close_txt,ylabel_txt,titletxt,footnote,power=power)
810
-
811
- return df2
812
-
813
- if __name__=='__main__':
814
- symbol='sh143595'
815
- fromdate='2019-1-1'
816
- todate='2020-3-30'
817
- ebp=exchange_bond_price('sh019521',fromdate,todate)
818
-
819
- #==============================================================================
820
- def exchange_covbond_deal(rank=10,option='1'):
821
- """
822
- 功能:获得沪深债券市场可转券即时行情
823
- 输入:从头开始显示的个数num;选项option:默认1按照交易时间排列,
824
- 2按照债券代码从小到大排列,3按照债券代码从大到小排列,4按照涨跌幅从高到低,
825
- 5按照涨跌幅从低到高,6按照成交量从高到低排列,7按照成交量从低到高排列。
826
- 其他选项按照默认排列。
827
- """
828
- num=rank
829
-
830
- print("开始搜索互联网,可能需要一点时间,请耐心等候......")
831
- #定义标准输出关闭类
832
- import os, sys
833
- class HiddenPrints:
834
- def __enter__(self):
835
- self._original_stdout = sys.stdout
836
- sys.stdout = open(os.devnull, 'w')
837
- def __exit__(self, exc_type, exc_val, exc_tb):
838
- sys.stdout.close()
839
- sys.stdout = self._original_stdout
840
-
841
- import pandas as pd
842
- df=pd.DataFrame()
843
- #抓取银行间市场债券报价
844
- import akshare as ak
845
- with HiddenPrints():
846
- try:
847
- df=ak.bond_zh_hs_cov_spot()
848
- except:
849
- pass
850
- if len(df)==0:
851
- print(" #Error(exchange_covbond_deal),failed to get info, pleae try later.")
852
- return None
853
-
854
- #选取需要的字段
855
- df1=df[['ticktime','symbol','name','trade','pricechange','open','high', \
856
- 'low','buy','sell','volume']]
857
- #转换字符类型到数值类型
858
- df1['trade']=df1['trade'].astype("float64")
859
- df1['pricechange']=df1['pricechange'].astype("float64")
860
- df1['volume']=df1['volume'].astype("int")
861
-
862
- #其他选项均作为默认选项
863
- if not option in ['1','2','3','4','5','6','7']: option='1'
864
- if option=='1':
865
- df1.sort_values(by=['ticktime'],ascending=[True],inplace=True)
866
- optiontxt=texttranslate("按照交易时间排序")
867
- if option=='2':
868
- df1.sort_values(by=['symbol'],ascending=[True],inplace=True)
869
- optiontxt=texttranslate("按照代码从小到大排序")
870
- if option=='3':
871
- df1.sort_values(by=['symbol'],ascending=[False],inplace=True)
872
- optiontxt=texttranslate("按照代码从大到小排序")
873
- if option=='4':
874
- df1.sort_values(by=['pricechange'],ascending=[False],inplace=True)
875
- optiontxt=texttranslate("按照涨跌幅从高到低排序")
876
- if option=='5':
877
- df1.sort_values(by=['pricechange'],ascending=[True],inplace=True)
878
- optiontxt=texttranslate("按照涨跌幅从低到高排序")
879
- if option=='6':
880
- df1.sort_values(by=['volume'],ascending=[False],inplace=True)
881
- optiontxt=texttranslate("按照成交量从高到低排序")
882
- if option=='7':
883
- df1.sort_values(by=['volume'],ascending=[True],inplace=True)
884
- optiontxt=texttranslate("按照成交量从低到高排序")
885
- #重新索引
886
- df1.reset_index(drop=True)
887
-
888
- df2=df1.rename(columns={'ticktime':texttranslate('时间'),'symbol':texttranslate('债券代码'), \
889
- 'name':texttranslate('债券名称'),'trade':texttranslate('成交价'),'pricechange':texttranslate('涨跌(元)'), \
890
- 'open':texttranslate('开盘价'),'high':texttranslate('最高价'),'low':texttranslate('最低价'), \
891
- 'buy':texttranslate('买入价'),'sell':texttranslate('卖出价'),'volume':texttranslate('成交量')})
892
- """
893
- print("\n***",texttranslate("沪深交易所可转债现券即时行情(")+optiontxt+texttranslate(",前")+str(num)+texttranslate("名)***"))
894
- import pandas as pd
895
- pd.set_option('display.unicode.ambiguous_as_wide', True)
896
- pd.set_option('display.unicode.east_asian_width', True)
897
- pd.set_option('display.width', 200) # 设置打印宽度(**重要**)
898
- print(df2.head(num).to_string(index=False))
899
-
900
- import datetime
901
- today = datetime.date.today().strftime("%Y-%m-%d")
902
- footnote="\n"+texttranslate("数据来源:新浪财经,")+today
903
- print(footnote)
904
- """
905
- titletxt=texttranslate("沪深交易所可转债现券即时行情(")+optiontxt+texttranslate(",前")+str(num)+texttranslate("名)")
906
- import datetime; todaydt = datetime.date.today().strftime("%Y-%m-%d")
907
- footnote="\n"+texttranslate("数据来源:新浪财经,")+todaydt
908
-
909
- df_display_CSS(df2.head(num),titletxt=titletxt,footnote=footnote,facecolor='papayawhip',decimals=3, \
910
- first_col_align='left',second_col_align='left', \
911
- last_col_align='center',other_col_align='center')
912
-
913
-
914
- return df1
915
-
916
- if __name__=='__main__':
917
- num=10
918
- option='1'
919
- ebd=exchange_covbond_deal(num,option)
920
- option='4'
921
- ebd=exchange_covbond_deal(num,option)
922
- option='5'
923
- ebd=exchange_covbond_deal(num,option)
924
- option='6'
925
- ebd=exchange_covbond_deal(num,option)
926
- option='7'
927
- ebd=exchange_covbond_deal(num,option)
928
-
929
-
930
- #==============================================================================
931
- if __name__=='__main__':
932
- symbol='sh019521'
933
- symbol='sh113565'
934
- symbol='sh019319'
935
-
936
- fromdate='2024-1-1'
937
- todate='2024-3-31'
938
-
939
- cov=exchange_covbond_price(symbol,fromdate,todate)
940
-
941
- #def exchange_covbond_price(symbol,fromdate,todate,power=0,graph=True):
942
- def exchange_covbond_price(ticker,start='MRY',end='today',power=0,graph=True):
943
- """
944
- 功能:获得沪深市场可转债历史成交行情
945
- 输入:沪深债券代码symbol,起始日期fromdate,截止日期todate。
946
- 返回:历史价格df
947
- 输出:折线图
948
- """
949
- symbol=ticker
950
- fromdate,todate=start_end_preprocess(start,end)
951
-
952
- print(" Searching for bond",symbol,"\b, it may take time ...")
953
-
954
- import pandas as pd
955
- import akshare as ak
956
- import datetime
957
-
958
- #检查日期期间的合理性
959
- result,start,end=check_period(fromdate, todate)
960
- if result is None: return None
961
-
962
- #变换代码格式
963
- symbol2=tickers_cvt2ak(symbol)
964
-
965
- #抓取历史行情
966
- try:
967
- df=ak.bond_zh_hs_cov_daily(symbol=symbol2)
968
- except:
969
- print(" #Error(exchange_covbond_price), failed to get info of",symbol)
970
- return None
971
-
972
- #过滤日期期间
973
- df['datepd']=df['date'].apply(lambda x: pd.to_datetime(x))
974
- df.set_index('datepd',inplace=True)
975
- df2=df[(df.index >= start) & (df.index <= end)]
976
- df2.rename(columns={'open':'Open','high':'High','low':'Low','close':'Close'},inplace=True)
977
-
978
- #绘图
979
- if graph:
980
- todaydt = datetime.date.today().strftime("%Y-%m-%d")
981
- titletxt1=text_lang('沪深债券行情:','Exchange Bond Price Trend: ')
982
- titletxt=titletxt1+get_exchange_bond_name_china2(symbol)
983
- close_txt=text_lang('收盘价','Close')
984
- ylabel_txt=text_lang('价格','Price')
985
- footnote0=text_lang('数据来源:新浪,','Data source: sina, ')
986
- footnote=footnote0+todaydt
987
-
988
- plot_line(df2,'Close',close_txt,ylabel_txt,titletxt,footnote,power=power)
989
-
990
- return df
991
-
992
- if __name__=='__main__':
993
- symbol='sh113565'
994
- fromdate='2020-1-1'
995
- todate='2020-5-6'
996
- ebp=exchange_covbond_price('sz128086',fromdate,todate)
997
-
998
- #==============================================================================
999
- if __name__=='__main__':
1000
- country='中国'
1001
- name='中国1年期国债'
1002
- fromdate='2020-1-1'
1003
- todate='2020-5-6'
1004
-
1005
- def country_bond_list(country="中国"):
1006
- """
1007
- 功能:获得各国政府债券列表
1008
- 输入:国家country
1009
- 返回:政府债券列表
1010
- 注意:无法获取数据
1011
- """
1012
- import akshare as ak
1013
- try:
1014
- bond_dict=ak.bond_investing_global_country_name_url(country=country)
1015
- except:
1016
- print(" #Error(country_bond_list), bonds not found for",country)
1017
- return None
1018
-
1019
- print("***",texttranslate(country),"\b"+texttranslate("政府债券列表"),"***")
1020
- bond_list=bond_dict.keys()
1021
- for b in bond_list:
1022
- print(" ",b)
1023
-
1024
- return
1025
-
1026
-
1027
- def country_bond_price(country,name,fromdate,todate,period="每日"):
1028
- """
1029
- 功能:获得全球政府债券市场历史成交行情
1030
- 输入:国家country,政府债券名称name,起始日期fromdate,截止日期todate。
1031
- 返回:历史价格df
1032
- 输出:折线图
1033
- 注意:无法获取数据
1034
- """
1035
- #检查日期期间的合理性
1036
- result,start,end=check_period(fromdate, todate)
1037
- start_date=start.strftime("%Y/%m/%d")
1038
- end_date=end.strftime("%Y/%m/%d")
1039
-
1040
- if result is None: return None
1041
-
1042
- #抓取历史行情
1043
- import akshare as ak
1044
- try:
1045
- """
1046
- #ak似乎不再支持这个函数了
1047
- df=ak.get_country_bond(country=country,index_name=name, \
1048
- start_date=start_date, end_date=end_date)
1049
- """
1050
- df=ak.bond_investing_global(country=country,index_name=name, \
1051
- period=period,start_date=start_date,end_date=end_date)
1052
- except:
1053
- print(" #Error(country_bond_price), failed to get info on",texttranslate(country),"\b,",texttranslate(name))
1054
- return None
1055
- df.sort_index(axis=0, ascending=True,inplace=True)
1056
-
1057
- #过滤日期期间
1058
- df1=df.drop(df[df.index < start].index)
1059
- df2=df1.drop(df1[df1.index > end].index)
1060
-
1061
- #绘图
1062
- titletxt=texttranslate('全球政府债券收盘价历史行情:')+name
1063
- import datetime
1064
- today = datetime.date.today().strftime("%Y-%m-%d")
1065
- footnote="\n"+texttranslate("数据来源:英为财情,")+today
1066
- plot_line(df2,'收盘',texttranslate('收盘价'),texttranslate('价格'),titletxt,footnote,power=4)
1067
-
1068
- return df
1069
-
1070
- if __name__=='__main__':
1071
- cbp=country_bond_price(country,name,fromdate,todate)
1072
-
1073
- #==============================================================================
1074
- def bond_eval(aytm,yper,c,fv=100,mterm=1):
1075
- """
1076
- 功能:计算债券的估值价格,即现值
1077
- 输入:
1078
- aytm: 年化折现率,年化市场利率
1079
- yper: 距离到期日的年数
1080
- c: 票面利率
1081
- fv: 票面价值
1082
- mterm: 每年付息期数,默认为1,期末付息
1083
- """
1084
- #每期折现率
1085
- rate=aytm/mterm
1086
- #每期票息
1087
- pmt=fv*c/mterm
1088
-
1089
- #循环计算现值
1090
- bvalue=0.0
1091
- for t in range(1,yper*mterm+1):
1092
- bvalue=bvalue+pmt/((1+rate)**t)
1093
- bvalue=bvalue+fv/((1+rate)**(yper*mterm))
1094
-
1095
- return bvalue
1096
-
1097
- if __name__=='__main__':
1098
- aytm=0.08
1099
- yper=3
1100
- fv=100
1101
- c=0.1
1102
- bvalue=bond_eval(aytm,yper,c,fv=100,mterm=1)
1103
-
1104
- #==============================================================================
1105
- def bond_malkiel1(aytm,yper,c,fv=100,mterm=1, \
1106
- bplist=[-100,-50,-20,-10,-5,5,10,20,50,100]):
1107
- """
1108
- 功能:计算债券的估值价格,即现值。演示债券估值定理一。
1109
- 输入:
1110
- aytm: 年化折现率,年化市场利率,年化到期收益率
1111
- yper: 距离到期日的年数
1112
- c: 年化票面利率
1113
- fv: 票面价值
1114
- mterm: 每年付息期数,默认为1,期末付息
1115
- bp: 到期收益率变化的基点数列表,100 bp = 1%
1116
- """
1117
- import pandas as pd
1118
- df=pd.DataFrame(columns=('bp','YTM','Price','xLabel'))
1119
- p0=round(bond_eval(aytm,yper,c,fv,mterm),2)
1120
- s=pd.Series({'bp':0,'YTM':aytm,'Price':p0,'xLabel':str(round(aytm*100,2))+'%'})
1121
- try:
1122
- df=df.append(s, ignore_index=True)
1123
- except:
1124
- df=df._append(s, ignore_index=True)
1125
-
1126
- #计算基点变化对于债券估计的影响
1127
- for b in bplist:
1128
- ay=aytm + b/10000.0
1129
- pb=round(bond_eval(ay,yper,c,fv,mterm),2)
1130
-
1131
- if b < 0:
1132
- xl='-'+str(abs(b))+'bp'
1133
- elif b > 0:
1134
- xl='+'+str(b)+'bp'
1135
- else:
1136
- xl=str(aytm*100)+'%'
1137
- s=pd.Series({'bp':b,'YTM':ay,'Price':pb,'xLabel':xl})
1138
- try:
1139
- df=df.append(s, ignore_index=True)
1140
- except:
1141
- df=df._append(s, ignore_index=True)
1142
-
1143
- #按照到期收益率升序排序
1144
- df.sort_values(by=['YTM'],ascending=[True],inplace=True)
1145
- #指定索引
1146
- df.reset_index(drop=True,inplace=True)
1147
-
1148
- #显示
1149
- df1=df.copy()
1150
- #df1['YTM%']=round(df1['YTM']*100,2)
1151
- df1['YTM%']=df1['YTM'].apply(lambda x:round(x*100,2))
1152
-
1153
- df2=df1[['xLabel','YTM%','Price']]
1154
- df3=df2.rename(columns={'xLabel':texttranslate('到期收益率变化'),'YTM%':texttranslate('到期收益率%'),'Price':texttranslate('债券价格')})
1155
- pd.set_option('display.unicode.ambiguous_as_wide', True)
1156
- pd.set_option('display.unicode.east_asian_width', True)
1157
- pd.set_option('display.width', 180) # 设置打印宽度(**重要**)
1158
-
1159
- lang=check_language()
1160
- if lang == 'English':
1161
- df4=df3.rename(columns={'到期收益率变化':'YTM Change','到期收益率%':'YTM%','债券价格':'Bond Price'})
1162
- else:
1163
- df4=df3
1164
- #print("\n",df4.to_string(index=False))
1165
- df_display_CSS(df4,titletxt='',footnote='',facecolor='papayawhip', \
1166
- first_col_align='center',second_col_align='center', \
1167
- last_col_align='center',other_col_align='center')
1168
-
1169
-
1170
- #绘图
1171
- plt.plot(df['xLabel'],df['Price'],color='red',marker='o')
1172
-
1173
- #绘制虚线
1174
- xpos=str(round(aytm*100,2))+'%'
1175
- ymax=max(df['Price'])
1176
- ymin=min(df['Price'])
1177
- plt.vlines(x=xpos,ymin=ymin,ymax=p0,ls=":",colors="blue")
1178
-
1179
- if lang == 'Chinese':
1180
- titletxt=texttranslate("债券价格与到期收益率的关系")
1181
- plt.ylabel(texttranslate("债券价格"),fontsize=ylabel_txt_size)
1182
- footnote1=texttranslate("到期收益率及其变化幅度")+"(100bp = 1%)"
1183
- footnote2="\n"+texttranslate("债券面值")+str(fv)+texttranslate(",票面利率")+str(round(c*100,2))+"%,"
1184
- footnote3=texttranslate("每年付息")+str(mterm)+texttranslate("次,到期年数")+str(yper)
1185
- footnote4=texttranslate(",到期收益率")+str(round(aytm*100,2))+"%"
1186
- else:
1187
- titletxt="Malkiel\'s Law 1: Relationship btw Bond Price & YTM"
1188
- plt.ylabel("Bond Price",fontsize=ylabel_txt_size)
1189
- footnote1="YTM(100bp = 1%) -->\n"
1190
- footnote2="Notes: Bond Par Value "+str(fv)+", Coupon Rate "+str(round(c*100,2))+"%.\n"
1191
- footnote3="Annually paid interest "+str(mterm)+" time(s). "
1192
- footnote4="Year(s) to maturity "+str(yper)+", YTM "+str(round(aytm*100,2))+"%"
1193
-
1194
- footnote=footnote1+footnote2+footnote3+footnote4
1195
- plt.title(titletxt,fontsize=title_txt_size,fontweight='bold')
1196
- plt.xlabel(footnote,fontsize=xlabel_txt_size)
1197
- #plt.tick_params(labelsize=11)
1198
- #plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
1199
- plt.xticks(rotation=30)
1200
-
1201
- plt.gca().set_facecolor('whitesmoke')
1202
- plt.show(); plt.close()
1203
-
1204
- return
1205
-
1206
- if __name__=='__main__':
1207
- aytm=0.08
1208
- yper=3
1209
- fv=100
1210
- c=0.1
1211
- mterm=1
1212
- bplist=[-100,-50,-20,-10,-5,5,10,20,50,100]
1213
- bond_malkiel1(aytm,yper,c,fv=100,mterm=1,bplist=bplist)
1214
-
1215
- #==============================================================================
1216
- def bond_malkiel2(aytm,yper,c,fv=100,mterm=1, \
1217
- yperlist=[1,2,5,10,20,50,100]):
1218
- """
1219
- 功能:计算债券估值价格的变化,演示债券估值定理二。
1220
- 输入:
1221
- aytm: 年化折现率,年化市场利率,年化到期收益率
1222
- yper: 距离到期日的年数
1223
- c: 年化票面利率
1224
- fv: 票面价值
1225
- mterm: 每年付息期数,默认为1,期末付息
1226
- yperlist: 债券的不同期限年数列表
1227
- """
1228
- import pandas as pd
1229
- df=pd.DataFrame(columns=('Maturity','YTM','Price','deltaPrice','xLabel'))
1230
- p0=round(bond_eval(aytm,yper,c,fv,mterm),2)
1231
- s=pd.Series({'Maturity':yper,'YTM':aytm,'Price':p0,'deltaPrice':0, \
1232
- 'xLabel':str(yper)+'年'})
1233
- try:
1234
- df=df.append(s, ignore_index=True)
1235
- except:
1236
- df=df._append(s, ignore_index=True)
1237
-
1238
- #计算基点变化对于债券估计的影响
1239
- for y in yperlist:
1240
- pb=round(bond_eval(aytm,y,c,fv,mterm),2)
1241
-
1242
- s=pd.Series({'Maturity':y,'YTM':aytm,'Price':pb,'deltaPrice':(pb-p0), \
1243
- 'xLabel':str(y)+'年'})
1244
- try:
1245
- df=df.append(s, ignore_index=True)
1246
- except:
1247
- df=df._append(s, ignore_index=True)
1248
-
1249
- #按照到期收益率升序排序
1250
- df.sort_values(by=['Maturity'],ascending=[True],inplace=True)
1251
- #指定索引
1252
- df.reset_index(drop=True,inplace=True)
1253
-
1254
- #显示
1255
- df1=df.copy()
1256
- df2=df1[['Maturity','deltaPrice']]
1257
- df3=df2.rename(columns={'Maturity':texttranslate('到期时间(年)'),'deltaPrice':texttranslate('债券价格变化')})
1258
- pd.set_option('display.unicode.ambiguous_as_wide', True)
1259
- pd.set_option('display.unicode.east_asian_width', True)
1260
- pd.set_option('display.width', 180) # 设置打印宽度(**重要**)
1261
-
1262
- lang=check_language()
1263
- if lang == 'English':
1264
- df4=df3.rename(columns={'到期时间(年)':'Year(s) to Maturity','债券价格变化':'Bond Price Change'})
1265
- else:
1266
- df4=df3
1267
- #print("\n",df4.to_string(index=False))
1268
- df_display_CSS(df4,titletxt='',footnote='',facecolor='papayawhip', \
1269
- first_col_align='center',second_col_align='center', \
1270
- last_col_align='center',other_col_align='center')
1271
-
1272
-
1273
- #绘图
1274
- plt.plot(df['Maturity'],df['deltaPrice'],color='red',marker='o')
1275
-
1276
- #绘制虚线
1277
- xpos=yper
1278
- ymax=0
1279
- ymin=min(df['deltaPrice'])
1280
- plt.vlines(x=xpos,ymin=ymin,ymax=0,ls=":",color="blue")
1281
- plt.axhline(y=0,ls=":",c="black")
1282
-
1283
- if lang == 'Chinese':
1284
- titletxt=texttranslate("债券价格的变化与到期时间的关系")
1285
- plt.ylabel(texttranslate("债券价格的变化"),fontsize=ylabel_txt_size)
1286
- footnote1=texttranslate("到期时间(年)")+"-->"
1287
- footnote2="\n"+texttranslate("债券面值")+str(fv)+texttranslate(",票面利率")+str(round(c*100,2))+"%,"
1288
- footnote3=texttranslate("每年付息")+str(mterm)+texttranslate("次,期限")+str(yper)+texttranslate("年")
1289
- footnote4=texttranslate(",到期收益率")+str(round(aytm*100,2))+"%"
1290
- else:
1291
- titletxt="Malkiel\'s Law 2: Relationship btw Bond Price Change & Time to Maturity"
1292
- plt.ylabel("Bond Price Change",fontsize=ylabel_txt_size)
1293
- footnote1="Year(s) to Maturity -->\n"
1294
- footnote2="Notes: Bond Par Value "+str(fv)+", Coupon Rate "+str(round(c*100,2))+"%.\n"
1295
- footnote3="Annualy paid interest "+str(mterm)+" time(s), Year(s) to maturity "+str(yper)
1296
- footnote4=", YTM "+str(round(aytm*100,2))+"%."
1297
-
1298
- footnote=footnote1+footnote2+footnote3+footnote4
1299
- plt.title(titletxt,fontsize=title_txt_size,fontweight='bold')
1300
- plt.xlabel(footnote,fontsize=xlabel_txt_size)
1301
- #plt.tick_params(labelsize=11)
1302
- plt.xticks(rotation=30)
1303
-
1304
- plt.gca().set_facecolor('whitesmoke')
1305
- plt.show(); plt.close()
1306
-
1307
- return
1308
-
1309
- if __name__=='__main__':
1310
- aytm=0.08
1311
- yper=3
1312
- fv=100
1313
- c=0.1
1314
- mterm=1
1315
- yperlist=[1,2,5,10,15,30]
1316
- bond_malkiel2(aytm,yper,c,fv,mterm,yperlist=yperlist)
1317
-
1318
- #==============================================================================
1319
- def bond_malkiel3(aytm,yper,c,fv=100,mterm=1):
1320
- """
1321
- 功能:计算债券的估值价格变化的速度,演示债券估值定理三。
1322
- 输入:
1323
- aytm: 年化折现率,年化市场利率,年化到期收益率
1324
- yper: 距离到期日的年数
1325
- c: 年化票面利率
1326
- fv: 票面价值
1327
- mterm: 每年付息期数,默认为1,期末付息
1328
- """
1329
- yperlist=list(range(1,yper*2+2))
1330
-
1331
- import pandas as pd
1332
- df=pd.DataFrame(columns=('Maturity','Price'))
1333
- #计算期限变化对于债券价格的影响
1334
- for y in yperlist:
1335
- pb=round(bond_eval(aytm,y,c,fv,mterm),2)
1336
- s=pd.Series({'Maturity':str(y),'Price':pb})
1337
- try:
1338
- df=df.append(s, ignore_index=True)
1339
- except:
1340
- df=df._append(s, ignore_index=True)
1341
-
1342
- #价格变化
1343
- df['deltaPrice']=df['Price'].shift(-1)-df['Price']
1344
- df.dropna(inplace=True)
1345
-
1346
- #价格与价格变化风险双轴折线图
1347
- fig = plt.figure()
1348
-
1349
- lang=check_language()
1350
- #绘制左侧纵轴
1351
- ax = fig.add_subplot(111)
1352
-
1353
- if lang == 'Chinese':
1354
- ax.plot(df['Maturity'],df['Price'],'-',label=texttranslate("债券价格"), \
1355
- linestyle='-',linewidth=2,color='blue')
1356
- ax.set_ylabel(texttranslate("债券价格"),fontsize=14)
1357
- footnote1=texttranslate("到期时间(年)")+"-->"
1358
- footnote2="\n"+texttranslate("债券面值")+str(fv)+texttranslate(",票面利率")+str(round(c*100,2))+"%,"
1359
- footnote3=texttranslate("每年付息")+str(mterm)+texttranslate("次,期限")+str(yper)+texttranslate("年")
1360
- footnote4=texttranslate(",到期收益率")+str(round(aytm*100,2))+"%"
1361
- else:
1362
- ax.plot(df['Maturity'],df['Price'],'-',label="Bond Price", \
1363
- linestyle='-',linewidth=2,color='blue')
1364
- ax.set_ylabel("Bond Price",fontsize=ylabel_txt_size)
1365
- footnote1="Year(s) to Maturity -->\n"
1366
- footnote2="Notes: Bond Par Value "+str(fv)+", Coupon Rate "+str(round(c*100,2))+"%.\n"
1367
- footnote3="Annually paid interest "+str(mterm)+" time(s), Year(s) to Maturity "+str(yper)
1368
- footnote4=", YTM "+str(round(aytm*100,2))+"%."
1369
-
1370
- footnote=footnote1+footnote2+footnote3+footnote4
1371
- ax.set_xlabel(footnote,fontsize=xlabel_txt_size)
1372
- ax.legend(loc='center left',fontsize=legend_txt_size)
1373
-
1374
- #绘制垂直虚线
1375
- xpos=yper-1
1376
- ymax=bond_eval(aytm,yper,c,fv,mterm)
1377
- ymin=min(df['Price'])
1378
- plt.vlines(x=xpos,ymin=ymin,ymax=ymax,ls=":",color="black")
1379
-
1380
- #绘制右侧纵轴
1381
- ax2 = ax.twinx()
1382
-
1383
- if lang == 'Chinese':
1384
- ax2.plot(df['Maturity'],df['deltaPrice'],'-',label=texttranslate("债券价格的变化速度"), \
1385
- linestyle='-.',linewidth=2,color='orange')
1386
- ax2.set_ylabel(texttranslate("债券价格的变化速度"),fontsize=ylabel_txt_size)
1387
- else:
1388
- ax2.plot(df['Maturity'],df['deltaPrice'],'-',label="Bond Price Change Speed", \
1389
- linestyle='-.',linewidth=2,color='orange')
1390
- ax2.set_ylabel("Bond Price Change Speed",fontsize=ylabel_txt_size)
1391
-
1392
- ax2.legend(loc='center right',fontsize=legend_txt_size)
1393
-
1394
- if lang == 'Chinese':
1395
- titletxt=texttranslate("债券到期时间与债券价格的变化速度")
1396
- else:
1397
- titletxt="Malkiel\'s Law 3: Relationship btw Time to Maturity & Bond Price Change Speed"
1398
-
1399
- plt.title(titletxt, fontsize=title_txt_size,fontweight='bold')
1400
-
1401
- plt.gca().set_facecolor('whitesmoke')
1402
- plt.show(); plt.close()
1403
-
1404
- return
1405
-
1406
- if __name__=='__main__':
1407
- aytm=0.08
1408
- yper=8
1409
- fv=100
1410
- c=0.1
1411
- mterm=2
1412
- bond_malkiel3(aytm,yper,c,fv,mterm)
1413
-
1414
- #==============================================================================
1415
- def bond_malkiel4(aytm,yper,c,fv=100,mterm=1, \
1416
- bplist=[-300,-250,-200,-150,-100,-50,50,100,150,200,250,300]):
1417
- """
1418
- 功能:计算债券的估值价格变化,演示债券估值定理四。
1419
- 输入:
1420
- aytm: 年化折现率,年化市场利率,年化到期收益率
1421
- yper: 距离到期日的年数
1422
- c: 年化票面利率
1423
- fv: 票面价值
1424
- mterm: 每年付息期数,默认为1,期末付息
1425
- """
1426
- #bplist=[-5,-4,-3,-2,-1,1,2,3,4,5]
1427
- import pandas as pd
1428
- df=pd.DataFrame(columns=('bp','YTM','Price','xLabel','deltaPrice'))
1429
- p0=bond_eval(aytm,yper,c,fv,mterm)
1430
- s=pd.Series({'bp':0,'YTM':aytm,'Price':p0,'xLabel':format(aytm*100,'.2f')+'%','deltaPrice':0})
1431
- try:
1432
- df=df.append(s, ignore_index=True)
1433
- except:
1434
- df=df._append(s, ignore_index=True)
1435
-
1436
- #计算基点变化对于债券估计的影响
1437
- for b in bplist:
1438
- ay=aytm + b/10000.0
1439
- pb=bond_eval(ay,yper,c,fv,mterm)
1440
-
1441
- if b < 0:
1442
- xl='-'+str(abs(b))+'bp'
1443
- elif b > 0:
1444
- xl='+'+str(b)+'bp'
1445
- else:
1446
- xl=str(aytm*100)+'%'
1447
- s=pd.Series({'bp':b,'YTM':ay,'Price':pb,'xLabel':xl,'deltaPrice':(pb-p0)})
1448
- try:
1449
- df=df.append(s, ignore_index=True)
1450
- except:
1451
- df=df._append(s, ignore_index=True)
1452
-
1453
- #按照到期收益率升序排序
1454
- df.sort_values(by=['YTM'],ascending=[True],inplace=True)
1455
- #指定索引
1456
- df.reset_index(drop=True,inplace=True)
1457
-
1458
- #拆分为收益率降低/上升两部分
1459
- df1=df[df['deltaPrice'] >= 0]
1460
- df2=df[df['deltaPrice'] <= 0]
1461
-
1462
- #将df2“两次翻折”,便于与df1比较
1463
- df3=df2.copy()
1464
- df3['deltaPrice1']=-df3['deltaPrice']
1465
- df3.sort_values(by=['YTM'],ascending=[False],inplace=True)
1466
- df3.reset_index(drop=True,inplace=True)
1467
- df3['xLabel1']=df3['xLabel'].apply(lambda x: x.replace('+','-'))
1468
-
1469
- #绘图
1470
- lang=check_language()
1471
- if lang == 'Chinese':
1472
- plt.plot(df1['xLabel'],df1['deltaPrice'],color='red',marker='o', \
1473
- label=texttranslate("收益率下降导致的债券价格增加"))
1474
- plt.plot(df2['xLabel'],df2['deltaPrice'],color='blue',marker='^', \
1475
- label=texttranslate("收益率上升导致的债券价格下降"))
1476
- plt.plot(df3['xLabel1'],df3['deltaPrice1'],':',color='blue',marker='<', \
1477
- label=texttranslate("收益率上升导致的债券价格下降(两次翻折后)"))
1478
- else:
1479
- plt.plot(df1['xLabel'],df1['deltaPrice'],color='red',marker='o', \
1480
- label="Increase in bond price due to decreasing YTM")
1481
- plt.plot(df2['xLabel'],df2['deltaPrice'],color='blue',marker='^', \
1482
- label=texttranslate("Decrease in bond price due to increasing YTM"))
1483
- plt.plot(df3['xLabel1'],df3['deltaPrice1'],':',color='blue',marker='<', \
1484
- label=texttranslate("Decrease in bond price due to increasing YTM(after 2 folds)"))
1485
-
1486
- plt.axhline(y=0,ls="-.",c="black", linewidth=1)
1487
-
1488
- #绘制垂直虚线
1489
- xpos=format(aytm*100,'.2f')+'%'
1490
- ymax=0
1491
- ymin=min(df['deltaPrice'])
1492
- plt.vlines(x=xpos,ymin=ymin,ymax=ymax,ls="-.",color="green",linewidth=1)
1493
- plt.legend(loc='best',fontsize=legend_txt_size)
1494
-
1495
- if lang == 'Chinese':
1496
- titletxt=texttranslate("到期收益率与债券价格变化的非对称性")
1497
- plt.ylabel(texttranslate("债券价格的变化"),fontsize=ylabel_txt_size)
1498
- footnote1=texttranslate("到期收益率及其变化幅度")+"(100bp = 1%)"
1499
- footnote2="\n"+texttranslate("债券面值")+str(fv)+texttranslate(",票面利率")+str(round(c*100,2))+"%,"
1500
- footnote3=texttranslate("每年付息")+str(mterm)+texttranslate("次,期限")+str(yper)+texttranslate("年")
1501
- footnote4=texttranslate(",到期收益率")+str(round(aytm*100,2))+"%"
1502
- else:
1503
- titletxt="Malkiel\'s Law 4: Asymmetry btw YTM & Change in Bond Price"
1504
- plt.ylabel("Change in Bond Price",fontsize=ylabel_txt_size)
1505
- footnote1="YTM(100bp = 1%) -->\n"
1506
- footnote2="Notes: Bond Par Value "+str(fv)+", Coupon Rate "+str(round(c*100,2))+"%.\n"
1507
- footnote3="Annually paid interest "+str(mterm)+" time(s), Year(s) to Maturity "+str(yper)
1508
- footnote4=", YTM "+str(round(aytm*100,2))+"%."
1509
-
1510
- footnote=footnote1+footnote2+footnote3+footnote4
1511
- plt.xlabel(footnote,fontsize=xlabel_txt_size)
1512
- #plt.tick_params(labelsize=11)
1513
- plt.xticks(rotation=30)
1514
- plt.title(titletxt,fontsize=title_txt_size,fontweight='bold')
1515
-
1516
- plt.gca().set_facecolor('whitesmoke')
1517
- plt.show(); plt.close()
1518
-
1519
- return
1520
-
1521
- if __name__=='__main__':
1522
- aytm=0.08
1523
- yper=3
1524
- fv=100
1525
- c=0.1
1526
- mterm=1
1527
- bond_malkiel4(aytm,yper,c,fv,mterm)
1528
-
1529
- #==============================================================================
1530
- def bond_malkiel5(aytm,yper,c,fv=100,mterm=1, \
1531
- clist=[-300,-250,-200,-150,-100,-50,50,100,150,200,250,300]):
1532
- """
1533
- 功能:计算债券的估值价格变化,演示债券估值定理五。
1534
- 输入:
1535
- aytm: 年化折现率,年化市场利率,年化到期收益率
1536
- yper: 距离到期日的年数
1537
- c: 年化票面利率
1538
- fv: 票面价值
1539
- mterm: 每年付息期数,默认为1,期末付息
1540
- """
1541
- #clist=[-300,-250,-200,-150,-100,-50,50,100,150,200,250,300]
1542
- import pandas as pd
1543
- df=pd.DataFrame(columns=('bp','c','Price','xLabel'))
1544
- p0=bond_eval(aytm,yper,c,fv,mterm)
1545
- s=pd.Series({'bp':0,'c':c,'Price':p0,'xLabel':format(c*100,'.2f')+'%'})
1546
- try:
1547
- df=df.append(s, ignore_index=True)
1548
- except:
1549
- df=df._append(s, ignore_index=True)
1550
-
1551
- #计算基点变化对于债券估计的影响
1552
- for b in clist:
1553
- cb=c + b/10000.0
1554
- if cb <= 0: continue
1555
- pb=bond_eval(aytm,yper,cb,fv,mterm)
1556
-
1557
- if b < 0:
1558
- xl='-'+str(abs(b))+'bp'
1559
- elif b > 0:
1560
- xl='+'+str(b)+'bp'
1561
- else:
1562
- xl=str(c*100)+'%'
1563
-
1564
- s=pd.Series({'bp':b,'c':cb,'Price':pb,'xLabel':xl})
1565
- try:
1566
- df=df.append(s, ignore_index=True)
1567
- except:
1568
- df=df._append(s, ignore_index=True)
1569
-
1570
- #按照到期收益率升序排序
1571
- df.sort_values(by=['c'],ascending=[True],inplace=True)
1572
- #指定索引
1573
- df.reset_index(drop=True,inplace=True)
1574
- #计算价格变化率
1575
- df['deltaPrice']=df['Price']-df['Price'].shift(1)
1576
- df['deltaPrice%']=df['Price'].pct_change()*100.0
1577
- df.dropna(inplace=True)
1578
-
1579
- #绘图
1580
- df1=df[df['bp'] <= 0]
1581
- df2=df[df['bp'] >= 0]
1582
- plt.plot(df1['xLabel'],df1['deltaPrice%'],color='red',marker='<')
1583
- plt.plot(df2['xLabel'],df2['deltaPrice%'],color='green',marker='>')
1584
-
1585
- #绘制垂直虚线
1586
- xpos=format(c*100,'.2f')+'%'
1587
- ymax=df[df['xLabel']==xpos]['deltaPrice%'].values[0]
1588
- ymin=min(df['deltaPrice%'])
1589
- plt.vlines(x=xpos,ymin=ymin,ymax=ymax,ls="-.",color="blue",linewidth=1)
1590
- #plt.legend(loc='best')
1591
-
1592
- lang=check_language()
1593
- if lang == 'Chinese':
1594
- titletxt=texttranslate("债券票息率与债券价格变化风险的关系")
1595
- plt.ylabel(texttranslate("债券价格的变化速度"),fontsize=ylabel_txt_size)
1596
- footnote1=texttranslate("票息率及其变化幅度")+"(100bp = 1%)-->"
1597
- footnote2="\n"+texttranslate("债券面值")+str(fv)+texttranslate(",票面利率")+str(round(c*100,2))+"%,"
1598
- footnote3=texttranslate("每年付息")+str(mterm)+texttranslate("次,期限")+str(yper)+texttranslate("年")
1599
- footnote4=texttranslate(",到期收益率")+str(round(aytm*100,2))+"%"
1600
- else:
1601
- titletxt="Malkiel\'s Law 5: Relationship btw Coupon Rate & Bond Price Risk"
1602
- plt.ylabel("Bond Price Change Speed (Risk)",fontsize=ylabel_txt_size)
1603
- footnote1="Coupon Rate(100bp = 1%) -->\n"
1604
- footnote2="Notes: Bond Par Value "+str(fv)+", Coupon Rate "+str(round(c*100,2))+"%,\n"
1605
- footnote3="Annually paid interest "+str(mterm)+" time(s), Year(s) to Maturity "+str(yper)
1606
- footnote4=", YTM "+str(round(aytm*100,2))+"%."
1607
-
1608
- footnote=footnote1+footnote2+footnote3+footnote4
1609
- plt.xlabel(footnote,fontsize=xlabel_txt_size)
1610
- #plt.tick_params(labelsize=11)
1611
- plt.xticks(rotation=30)
1612
- plt.title(titletxt,fontsize=title_txt_size,fontweight='bold')
1613
-
1614
- plt.gca().set_facecolor('whitesmoke')
1615
- plt.show(); plt.close()
1616
-
1617
- return
1618
-
1619
- if __name__=='__main__':
1620
- aytm=0.08
1621
- yper=8
1622
- fv=100
1623
- c=0.07
1624
- mterm=2
1625
- dp=bond_malkiel5(aytm,yper,c,fv,mterm)
1626
-
1627
- #==============================================================================
1628
- def cf_month(c,x,n,f=2,r=0.03):
1629
- """
1630
- 功能:计算国债期货的转换因子。
1631
- 输入:
1632
- c: 可交割国债的票面利率
1633
- x: 交割月到下一付息月的月份数
1634
- n: 剩余付息次数
1635
- f: 每年付息次数,默认2次
1636
- r: 5年期国债期货合约票面利率,默认3%
1637
- """
1638
- p1=(1+r/f)**(x*f/12)
1639
- p2=c/f
1640
- p3=c/r
1641
- p4=1-p3
1642
- p5=(1+r/f)**(n-1)
1643
- p6=1-x*f/12
1644
-
1645
- cf=(1/p1)*(p2+p3+p4/p5)-p2*p6
1646
-
1647
- return round(cf,4)
1648
-
1649
- if __name__=='__main__':
1650
- c=0.026
1651
- x=1
1652
- n=11
1653
- f=2
1654
- r=0.03
1655
- cf_month(c,x,n)
1656
-
1657
- #==============================================================================
1658
- def cf_day(c,v,m,f=2,r=0.03):
1659
- """
1660
- 功能:计算国债期货的转换因子。
1661
- 输入:
1662
- c: 年化票面利率
1663
- v: 到下一付息日的天数
1664
- m: 下一付息日后剩余的付息次数
1665
- f: 每年付息次数,默认2次
1666
- stdrate: 标准利率,默认3%
1667
- """
1668
- #基本折现因子
1669
- p=1/(1+r/f)
1670
- a=p**(v*f/365)
1671
- e=(c/f)*(p*(1-p**m))/(1-p)
1672
- d=p**m
1673
- b=(1-v*f/365)*(c/f)
1674
-
1675
- #假定票面价值为1元
1676
- cf=a*(c/f+e+d)-b
1677
-
1678
- return round(cf,4)
1679
-
1680
- if __name__=='__main__':
1681
- c=0.026
1682
- v=30
1683
- m=10
1684
- f=2
1685
- r=0.03
1686
- cf_day(c,v,m)
1687
-
1688
- #==============================================================================
1689
- if __name__=='__main__':
1690
- clist=[0.02,0.0225,0.025,0.0275,0.03,0.035,0.04,0.05,0.06]
1691
- v=30
1692
- m=10
1693
- f=2
1694
- r=0.03
1695
-
1696
- def cf_day_coupon_trend(clist,v,m,f=2,r=0.03):
1697
- """
1698
- 功能:计算国债期货的转换因子。
1699
- 输入:
1700
- clist: 债券票息率列表(年化票面利率)
1701
- v: 到下一付息日的天数
1702
- m: 下一付息日后剩余的付息次数
1703
- f: 每年付息次数,默认2次
1704
- stdrate: 标准利率,默认3%
1705
- """
1706
-
1707
- #检查clist是否列表
1708
- if not isinstance(clist,list):
1709
- print(" #Error(cf_day_coupon_trend): not a list of rates from",clist)
1710
- return None
1711
- if len(clist) < 3:
1712
- print(" #Error(cf_day_coupon_trend): not enough rates for showing trend",clist)
1713
- return None
1714
-
1715
- #计算各个票息率的转换因子
1716
- import pandas as pd
1717
- df=pd.DataFrame(columns=('c','v','m','f','r','cf'))
1718
- for c in clist:
1719
- cf=cf_day(c,v,m,f,r)
1720
- s=pd.Series({'c':c,'v':v,'m':m,'f':f,'r':r,'cf':cf})
1721
- try:
1722
- df=df.append(s, ignore_index=True)
1723
- except:
1724
- df=df._append(s, ignore_index=True)
1725
-
1726
- #按照到期收益率升序排序
1727
- df.sort_values(by=['c'],ascending=[True],inplace=True)
1728
- #指定索引
1729
- df['crate']=df['c']
1730
- df.set_index(['crate'],inplace=True)
1731
-
1732
- #打印
1733
- print("\n***",texttranslate("债券票息率对转换因子的影响"),"***")
1734
- print(texttranslate("名义券利率 :"),r)
1735
- print(texttranslate("每年付息次数 :"),f)
1736
- print(texttranslate("到下个付息日的天数 :"),v)
1737
- print(texttranslate("下个付息日后剩余的付息次数 :"),m)
1738
-
1739
- df1=df[['c','cf']].copy()
1740
- df2=df1.rename(columns={'c':texttranslate('债券票息率'),'cf':texttranslate('转换因子')})
1741
- pd.set_option('display.unicode.ambiguous_as_wide', True)
1742
- pd.set_option('display.unicode.east_asian_width', True)
1743
- pd.set_option('display.width', 180) # 设置打印宽度(**重要**)
1744
- print("\n",df2.to_string(index=False))
1745
-
1746
- #绘图
1747
- colname='cf'
1748
- collabel=texttranslate('债券的转换因子')
1749
- ylabeltxt=texttranslate('转换因子')
1750
- titletxt=texttranslate("债券票息率对转换因子的影响")
1751
- footnote=texttranslate('票息率')+' -->'+ \
1752
- "\n"+texttranslate("【债券描述】名义券利率:")+str(r)+texttranslate(', 每年付息次数:')+str(f)+ \
1753
- "\n"+texttranslate("到下个付息日的天数:")+str(v)+", "+texttranslate("下一付息日后剩余的付息次数:")+str(m)
1754
- plot_line(df,colname,collabel,ylabeltxt,titletxt,footnote)
1755
-
1756
- return df
1757
-
1758
- if __name__=='__main__':
1759
- clist=[0.0225,0.025,0.0275,0.03,0.04,0.06,0.08,0.1]
1760
- v=30
1761
- m=10
1762
- df=cf_day_coupon_trend(clist,v,m)
1763
-
1764
- #==============================================================================
1765
- if __name__=='__main__':
1766
- c=0.026
1767
- v=30
1768
- mlist=[4,6,8,10,12,14,16]
1769
- f=2
1770
- r=0.03
1771
-
1772
- def cf_day_remain_trend(c,v,mlist,f=2,r=0.03):
1773
- """
1774
- 功能:计算国债期货的转换因子。
1775
- 输入:
1776
- c: 债券票息率(年化票面利率)
1777
- v: 到下一付息日的天数
1778
- mlist: 下一付息日后剩余的付息次数列表
1779
- f: 每年付息次数,默认2次
1780
- stdrate: 名义券利率,默认3%
1781
- """
1782
-
1783
- #检查mlist是否列表
1784
- if not isinstance(mlist,list):
1785
- print("#Error(cf_day_remain_trend): not a list of payment times",mlist)
1786
- return None
1787
- if len(mlist) < 3:
1788
- print("#Error(cf_day_remain_trend): not enough times for showing trend",mlist)
1789
- return None
1790
-
1791
- #计算各个票息率的转换因子
1792
- import pandas as pd
1793
- df=pd.DataFrame(columns=('c','v','m','f','r','cf'))
1794
- for m in mlist:
1795
- cf=cf_day(c,v,m,f,r)
1796
- s=pd.Series({'c':c,'v':v,'m':m,'f':f,'r':r,'cf':cf})
1797
- try:
1798
- df=df.append(s, ignore_index=True)
1799
- except:
1800
- df=df._append(s, ignore_index=True)
1801
-
1802
- #按照到期收益率升序排序
1803
- df.sort_values(by=['m'],ascending=[True],inplace=True)
1804
- #指定索引
1805
- df['mtimes']=df['m']
1806
- df.set_index(['mtimes'],inplace=True)
1807
-
1808
- #打印
1809
- print("\n"+texttranslate("到期期限对债券转换因子的影响"))
1810
- print(texttranslate("名义券利率 :"),r)
1811
- print(texttranslate("债券票面利率 :"),c)
1812
- print(texttranslate("每年付息次数 :"),f)
1813
- print(texttranslate("到下个付息日的天数 :"),v)
1814
-
1815
- df1=df[['m','cf']].copy()
1816
- df2=df1.rename(columns={'m':texttranslate('债券到期期限*'),'cf':texttranslate('转换因子')})
1817
- pd.set_option('display.unicode.ambiguous_as_wide', True)
1818
- pd.set_option('display.unicode.east_asian_width', True)
1819
- pd.set_option('display.width', 180) # 设置打印宽度(**重要**)
1820
- print("\n",df2.to_string(index=False))
1821
- print(texttranslate("*指下一付息日后剩余的付息次数"))
1822
-
1823
- #绘图
1824
- colname='cf'
1825
- collabel=texttranslate('债券的转换因子')
1826
- ylabeltxt=texttranslate('转换因子')
1827
- titletxt=texttranslate("到期期限对债券转换因子的影响")
1828
- footnote=texttranslate('下一付息日后剩余的付息次数')+' -->'+ \
1829
- "\n"+texttranslate("【债券描述】名义券利率:")+str(r)+texttranslate(", 债券票面利率:")+str(c)+texttranslate(', 每年付息次数:')+str(f)+ \
1830
- "\n"+texttranslate("到下一付息日的天数:")+str(v)
1831
- plot_line(df,colname,collabel,ylabeltxt,titletxt,footnote)
1832
-
1833
- return df
1834
-
1835
- if __name__=='__main__':
1836
- df=cf_day_remain_trend(c,v,mlist)
1837
-
1838
- #==============================================================================
1839
- #==============================================================================
1840
- #==============================================================================
1841
- # 以下内容来自中债信息网
1842
- #==============================================================================
1843
- import requests
1844
- import datetime
1845
- UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36'
1846
- CHINABOND_TERM_MAP = {
1847
- '0': '总值',
1848
- '1': '1年以下',
1849
- '2': '1-3年',
1850
- '3': '3-5年',
1851
- '4': '5-7年',
1852
- '5': '7-10年',
1853
- '6': '10年以上',
1854
- }
1855
-
1856
- def get_chinabond_index_list():
1857
- """
1858
- 功能:获取债券指数列表,来源:中国债券信息网
1859
- 问题:数据源网址可能已经变化,目前无法抓取数据
1860
- """
1861
- headers = {
1862
- 'Referer': 'https://yield.chinabond.com.cn/',
1863
- 'User-Agent': UA,
1864
- }
1865
-
1866
- url = 'https://yield.chinabond.com.cn/cbweb-mn/indices/queryTree'
1867
- params = {
1868
- 'locale': 'zh_CN',
1869
- }
1870
- try:
1871
- r = requests.post(url, data=params, headers=headers, timeout=10)
1872
- except requests.exceptions.RequestException:
1873
- print(" #Error(get_chinabond_index_list): spider failed with return",r.text)
1874
- return None
1875
-
1876
- try:
1877
- data = r.json()
1878
- except:
1879
- print(" #Error(get_chinabond_index_list): failed to decode json.")
1880
- return None
1881
-
1882
- indexes = [i for i in data if i['isParent'] == 'false']
1883
-
1884
- return indexes
1885
-
1886
- if __name__=='__main__':
1887
- indexlist=get_chinabond_index_list()
1888
-
1889
- #==============================================================================
1890
- if __name__=='__main__':
1891
- keystr='国债'
1892
-
1893
- def search_bond_index_china(keystr='国债',printout=True):
1894
- """
1895
- 功能:基于关键词搜索中债指数名字
1896
- """
1897
- print(" Searching China bond index names with keyword",keystr,'......')
1898
-
1899
- indexlist=get_chinabond_index_list()
1900
- if indexlist is None:
1901
- print(" #Error(search_bond_index_china): no bond info found for",keystr)
1902
- if printout: return
1903
- else: return None
1904
-
1905
- import pandas as pd
1906
- indexdf=pd.DataFrame(indexlist)
1907
-
1908
- subdf=indexdf[indexdf['name'].str.contains(keystr)]
1909
-
1910
- subdflen=len(subdf)
1911
- if subdflen == 0:
1912
- print(" Sorry, bond index name(s) not found with keyword",keystr,'\b:-(')
1913
- keylist1=['国债','政府债','金融债','信用债','企业债','绿色债','铁路债']
1914
- keylist2=['利率债','路债','行债','区债','央票','短融','综合','银行间']
1915
- keylist=keylist1+keylist2
1916
- print(" Try one of these keywords:",keylist)
1917
-
1918
- if printout: return
1919
- else: return None
1920
-
1921
- if printout:
1922
- print(subdf['name'].to_string(index=False))
1923
- print(" Found",subdflen,"China bond index names with keyword",keystr,'\b:-)')
1924
- return
1925
- else: return subdf
1926
-
1927
-
1928
- if __name__=='__main__':
1929
- search_bond_index_china(keystr='国债')
1930
- search_bond_index_china(keystr='综合')
1931
- search_bond_index_china(keystr='银行间')
1932
- #==============================================================================
1933
- if __name__=='__main__':
1934
- name='中债-综合指数'
1935
- fromdate='2020-1-1'
1936
- todate='2021-2-8'
1937
- graph=True
1938
- power=6
1939
-
1940
- def bond_index_china(name,fromdate,todate,graph=True,power=5):
1941
- """
1942
- 功能:获取中债债券指数的价格,按日期升序排列
1943
- """
1944
- #检查日期区间的合理性
1945
- result,start,end=check_period(fromdate,todate)
1946
- if not result:
1947
- print(" #Error(bond_index_china): invalid date period from",fromdate,'to',todate)
1948
- if graph: return
1949
- else: return None
1950
-
1951
- #将债券指数名字转换成中债网的债券指数id
1952
- subdf=search_bond_index_china(keystr=name,printout=False)
1953
- if subdf is None:
1954
- print(" #Error(bond_index_china): none bond index found for",name)
1955
- if graph: return
1956
- else: return None
1957
-
1958
- subdflen=len(subdf)
1959
- #错误:未找到债券指数名字
1960
- if subdflen == 0:
1961
- print(" #Error(bond_index_china): empty bond index found for",name)
1962
- if graph: return
1963
- else: return None
1964
- #错误:找到多个债券指数名字
1965
- if subdflen > 1:
1966
- print(" #Error(bond_index_china): found more than one bond indexes")
1967
- print(subdf['name'].to_string(index=False))
1968
- if graph: return
1969
- else: return None
1970
-
1971
- #基于指数id提取历史价格
1972
- indexid=subdf['id'].values[0]
1973
- indexdictlist=get_chinabond_index(indexid)
1974
- if indexdictlist is None:
1975
- return None
1976
-
1977
- import pandas as pd
1978
- newname=name+"-总值-财富"
1979
- for i in indexdictlist:
1980
- if i['name'] == newname:
1981
- idf=pd.DataFrame(i['history'])
1982
- break
1983
-
1984
- #整理历史价格
1985
- idf.columns=['Date','Close']
1986
- idf['date']=pd.to_datetime(idf['Date'])
1987
- idf.set_index(['date'],inplace=True)
1988
- idf['Adj Close']=idf['Close']
1989
- idf['ticker']=name
1990
- idf['footnote']=''
1991
- idf['source']=texttranslate('中国债券信息网')
1992
-
1993
- idf1=idf[idf.index >= start]
1994
- idf2=idf1[idf1.index < end]
1995
-
1996
- num=len(idf2)
1997
- print(" Successfully retrieved",num,"records for",name)
1998
-
1999
- #不绘图
2000
- if not graph: return idf2
2001
- #绘图
2002
- colname='Close'
2003
- collabel=name
2004
- ylabeltxt=texttranslate('指数点数')
2005
- titletxt=texttranslate("中国债券价格指数走势")
2006
-
2007
- import datetime as dt; today=dt.date.today()
2008
- footnote=texttranslate("数据来源:中债登/中国债券信息网,")+str(today)
2009
- plot_line(idf2,colname,collabel,ylabeltxt,titletxt,footnote,power=power)
2010
-
2011
- return
2012
-
2013
- if __name__=='__main__':
2014
- bond_index_china('中债-综合指数','2020-1-1','2021-2-8')
2015
- bond_index_china('中债-国债总指数','2020-1-1','2021-2-8')
2016
- bond_index_china('中债-交易所国债指数','2020-1-1','2021-2-8')
2017
- bond_index_china('中债-银行间国债指数','2020-1-1','2021-2-8')
2018
- bond_index_china('中债-银行间债券总指数','2020-1-1','2021-2-8')
2019
-
2020
-
2021
- #==============================================================================
2022
- #@functools.lru_cache
2023
- def get_chinabond_index_id_name_map():
2024
- indexes = get_chinabond_index_list()
2025
- if indexes is None:
2026
- return None
2027
-
2028
- id_nam_map = {i['id']: i for i in indexes}
2029
- return id_nam_map
2030
-
2031
- if __name__=='__main__':
2032
- indexnamelist=get_chinabond_index_id_name_map()
2033
-
2034
- #==============================================================================
2035
-
2036
- def get_chinabond_index(indexid):
2037
-
2038
- """
2039
- 基于中债指数索引编号抓取指数历史数据
2040
- """
2041
-
2042
- headers = {
2043
- #'Referer': 'http://yield.chinabond.com.cn/',
2044
- 'Referer': 'https://yield.chinabond.com.cn',
2045
- 'User-Agent': UA,
2046
- }
2047
-
2048
- #url = 'http://yield.chinabond.com.cn/cbweb-mn/indices/singleIndexQuery'
2049
- #url = 'https://yield.chinabond.com.cn/cbweb-mn/indices/singleIndexQuery'
2050
- url = 'https://yield.chinabond.com.cn/cbweb-mn/indices/single_index_query?locale=zh_CN'
2051
- params = {
2052
- 'indexid': indexid,
2053
- 'zslxt': 'CFZS',
2054
- 'qxlxt': '0,1,2,3,4,5,6',
2055
- 'lx': '1',
2056
- 'locale': 'zh_CN',
2057
- }
2058
- # zslxt 指数类型,可以多个
2059
- # CFZS 财富指数
2060
- # JJZS 净价指数
2061
- # QJZS 全价指数
2062
- ##
2063
- # qxlxt 期限类型
2064
- # 0 总值
2065
- # 1 1年以下
2066
- # 2 1-3年
2067
- # 3 3-5年
2068
- # 4 5-7年
2069
- # 5 7-10年
2070
- # 6 10年以上
2071
- try:
2072
- #r = requests.post(url, data=params, headers=headers, timeout=4)
2073
- r = requests.post(url, data=params, headers=headers, timeout=4)
2074
- except requests.exceptions.RequestException:
2075
- #r = requests.post(url, data=params, headers=headers, timeout=10)
2076
- r = requests.post(url, data=params, headers=headers, timeout=10)
2077
-
2078
- try:
2079
- data = r.json()
2080
- except:
2081
- print(" #Error(get_chinabond_index): inaccessible to bond index id",indexid)
2082
- return None
2083
-
2084
- import datetime as dt
2085
- indexes = []
2086
- index_id_name_map = get_chinabond_index_id_name_map()
2087
- index_name = index_id_name_map[indexid]['name']
2088
- for key in data:
2089
- if not data[key]:
2090
- continue
2091
- if key.startswith('CFZS_'):
2092
- type_ = '财富'
2093
- term = CHINABOND_TERM_MAP[key[5:]]
2094
- else:
2095
- continue
2096
- name = f'{index_name}-{term}-{type_}'
2097
- history = []
2098
- for ts, val in data[key].items():
2099
- ts = dt.datetime.fromtimestamp(int(ts) / 1000).strftime('%Y-%m-%d')
2100
- history.append([ts, val])
2101
- history.sort(key=lambda x: x[0])
2102
-
2103
- index = {
2104
- 'source': 'chinabond',
2105
- 'code': name,
2106
- 'indexid': indexid,
2107
- 'name': name,
2108
- 'history': history,
2109
- }
2110
-
2111
- indexes.append(index)
2112
-
2113
- return indexes
2114
-
2115
- #==============================================================================
2116
- #==============================================================================
2117
- #==============================================================================
2118
- # 债券违约估计:KPMG风险中性定价模型
2119
- #==============================================================================
2120
- if __name__=='__main__':
2121
- k1=0.15
2122
- theta=0.8
2123
- i1=0.05
2124
-
2125
- def calc_kpmg_rnpm1(k1,theta,i1):
2126
- """
2127
- 功能:基于KPMG风险中性定价各个因素计算违约概率PD和预期损失率ELR
2128
- k1:票面利率coupon rate,注意不能低于rf
2129
- theta:违约时的回收率recovery rate at default。loss given default lgd=1-rrd
2130
- theta为零时表示违约时无可回收的资产
2131
- i1:无风险收益率risk-free rate,注意不能高于cr
2132
-
2133
- 局限性:仅适用于1年期债券,多期债券可以使用累积概率方法进行推算
2134
- """
2135
- #检查k1与i1之间的合理关系
2136
- if k1 < i1:
2137
- print(" #Warning(): coupon rate should not be lower than risk-free rate",k1,i1)
2138
- return None,None
2139
-
2140
- lgd=1-theta
2141
- pd=(k1-i1)/((1+k1)*lgd)
2142
- elr=pd*lgd
2143
-
2144
- return round(pd,4),round(elr,4)
2145
-
2146
- if __name__=='__main__':
2147
- calc_kpmg_rnpm1(k1,theta,i1)
2148
- calc_kpmg_rnpm1(0.05,0.95,0.015)
2149
- calc_kpmg_rnpm1(0.035,0.9,0.028)
2150
- calc_kpmg_rnpm1(0.03,0.8,0.025)
2151
-
2152
- #==============================================================================
2153
- if __name__=='__main__':
2154
- k1=0.15
2155
- theta=0.8
2156
- i1=0.05
2157
- k1list=[-300,-200,-150,-100,-80,-60,-40,-20,20,40,60,80,100,150,200,300]
2158
- loc1='upper left'
2159
- loc2='lower right'
2160
-
2161
- def kpmg_rnpm1_cr(k1,theta,i1, \
2162
- k1list=[-300,-200,-150,-100,-80,-60,-40,-20,20,40,60,80,100,150,200,300], \
2163
- loc1='best',loc2='best'):
2164
- """
2165
- 功能:展示KPMG风险中性定价债券票面利率对于债券违约概率pd=(1-P1)和预期损失率ELR的影响
2166
- k1:票面利率coupon rate,注意不能低于rf
2167
- theta:违约时的回收率recovery rate at default。loss given default lgd=1-theta
2168
- theta为零时表示违约时无可回收的资产
2169
- i1:无风险收益率risk-free rate,注意不能高于cr
2170
- k1list:以bp为单位围绕cr的变化值,不包括k1本身
2171
- """
2172
-
2173
- #生成k1list
2174
-
2175
- #生成EDR和ELR
2176
- import pandas as pd
2177
- df=pd.DataFrame(columns=('k1','changeInBP','theta','i1','pd','elr'))
2178
-
2179
- pdt,elr=calc_kpmg_rnpm1(k1,theta,i1)
2180
- xl=format(k1*100,'.2f')+'%'
2181
- s=pd.Series({'k1':xl,'changeInBP':0,'theta':theta,'i1':i1,'pd':pdt*100,'elr':elr*100})
2182
- try:
2183
- df=df.append(s, ignore_index=True)
2184
- except:
2185
- df=df._append(s, ignore_index=True)
2186
-
2187
-
2188
- #计算k1变化对于债券pd和elr的影响
2189
- for i in k1list:
2190
- k1t=k1 + i/10000.0
2191
- if k1t < i1: continue
2192
-
2193
- pdt,elr=calc_kpmg_rnpm1(k1t,theta,i1)
2194
- if pdt >= 1:
2195
- continue
2196
-
2197
- if i < 0:
2198
- xl='-'+str(abs(i))+'bp'
2199
- elif i > 0:
2200
- xl='+'+str(i)+'bp'
2201
- else:
2202
- xl=str(k1t*100)+'%'
2203
-
2204
- s=pd.Series({'k1':xl,'changeInBP':i,'theta':theta,'i1':i1,'pd':pdt*100,'elr':elr*100})
2205
- try:
2206
- df=df.append(s, ignore_index=True)
2207
- except:
2208
- df=df._append(s, ignore_index=True)
2209
-
2210
- #按照到期收益率升序排序
2211
- df.sort_values(by=['changeInBP'],ascending=[True],inplace=True)
2212
- #指定索引
2213
- df.reset_index(drop=True,inplace=True)
2214
-
2215
- #绘图
2216
- fig = plt.figure()
2217
-
2218
- ax = fig.add_subplot(111)
2219
- label1txt='违约概率%'
2220
- ax.plot(df['k1'],df['pd'],color='red',marker='<',label=label1txt)
2221
-
2222
- footnote1="\n"+"票息率"+"(100bp = 1%)-->"
2223
- footnote2="\n"+"债券票面利率初始值"+str(round(k1*100,2))+"%,违约回收率"+str(round(theta*100))+"%,"
2224
- footnote3="无风险利率"+str(round(i1*100,2))+'%'
2225
- footnote=footnote1+footnote2+footnote3
2226
-
2227
- ax.set_xlabel(footnote,fontsize=xlabel_txt_size)
2228
- ax.set_ylabel(label1txt,fontsize=ylabel_txt_size)
2229
- ax.legend(loc=loc1,fontsize=legend_txt_size)
2230
-
2231
- ax2 = ax.twinx()
2232
-
2233
- #设置第2纵轴的刻度范围,以便当第2条曲线与第1条曲线重合时能够区分开
2234
- ax2ymin=df['elr'].min()
2235
- ax2ymax=df['elr'].max()
2236
- ax2ymax=ax2ymax * 1.05
2237
- ax2.set_ylim([ax2ymin, ax2ymax])
2238
-
2239
- label2txt='预期损失率%'
2240
-
2241
- ax2.plot(df['k1'],df['elr'],color='green',marker='>',label=label2txt)
2242
-
2243
- ax2.set_ylabel(label2txt,fontsize=ylabel_txt_size)
2244
- ax2.legend(loc=loc2,fontsize=legend_txt_size)
2245
-
2246
- titletxt="KPMG风险中性定价:票面利率对债券违约估计的影响"
2247
- plt.title(titletxt,fontsize=title_txt_size,fontweight='bold')
2248
-
2249
- plt.xticks(rotation=30)
2250
-
2251
- plt.gca().set_facecolor('whitesmoke')
2252
- plt.show(); plt.close()
2253
-
2254
- return df
2255
-
2256
- if __name__ == "__main__":
2257
- df=kpmg_rnpm1_cr(k1,theta,i1,loc1='upper left',loc2='lower right')
2258
- #==============================================================================
2259
- if __name__=='__main__':
2260
- k1=0.15
2261
- theta=0.8
2262
- i1=0.05
2263
- i1list=[-200,-100,-50,-30,-20,-15,-10,-5,5,10,15,20,30,50,100,200]
2264
- loc1='upper left'
2265
- loc2='lower right'
2266
-
2267
- def kpmg_rnpm1_rf(k1,theta,i1, \
2268
- i1list=[-200,-100,-50,-30,-20,-15,-10,-5,5,10,15,20,30,50,100,200], \
2269
- loc1='best',loc2='best'):
2270
- """
2271
- 功能:展示KPMG风险中性定价无风险利率对于债券违约概率pd=(1-P1)和预期损失率ELR的影响
2272
- k1:票面利率coupon rate,注意不能低于rf
2273
- theta:违约时的回收率recovery rate at default。loss given default lgd=1-theta
2274
- theta为零时表示违约时无可回收的资产
2275
- i1:无风险收益率risk-free rate,注意不能高于cr,一般也不能低于零
2276
- i1list:以bp为单位围绕i1的变化值,不包括i1本身
2277
- """
2278
-
2279
- #生成1list
2280
-
2281
- #生成EDR和ELR
2282
- import pandas as pd
2283
- df=pd.DataFrame(columns=('k1','theta','i1','changeInBP','pd','elr'))
2284
-
2285
- pdt,elr=calc_kpmg_rnpm1(k1,theta,i1)
2286
- xl=format(i1*100,'.2f')+'%'
2287
- s=pd.Series({'k1':k1,'theta':theta,'i1':xl,'changeInBP':0,'pd':pdt*100,'elr':elr*100})
2288
- try:
2289
- df=df.append(s, ignore_index=True)
2290
- except:
2291
- df=df._append(s, ignore_index=True)
2292
-
2293
-
2294
- #计算i1变化对于债券pd和elr的影响
2295
- for i in i1list:
2296
- i1t=i1 + i/10000.0
2297
- if (i1t >= k1) | (i1t <0): continue
2298
-
2299
- pdt,elr=calc_kpmg_rnpm1(k1,theta,i1t)
2300
- if pdt >= 1:
2301
- continue
2302
-
2303
- if i < 0:
2304
- xl='-'+str(abs(i))+'bp'
2305
- elif i > 0:
2306
- xl='+'+str(i)+'bp'
2307
- else:
2308
- xl=str(i1t*100)+'%'
2309
-
2310
- s=pd.Series({'k1':k1,'theta':theta,'i1':xl,'changeInBP':i,'pd':pdt*100,'elr':elr*100})
2311
- try:
2312
- df=df.append(s, ignore_index=True)
2313
- except:
2314
- df=df._append(s, ignore_index=True)
2315
-
2316
- #按照到期收益率升序排序
2317
- df.sort_values(by=['changeInBP'],ascending=[True],inplace=True)
2318
- #指定索引
2319
- df.reset_index(drop=True,inplace=True)
2320
-
2321
- #绘图
2322
- fig = plt.figure()
2323
-
2324
- ax = fig.add_subplot(111)
2325
- label1txt='违约概率%'
2326
- ax.plot(df['i1'],df['pd'],color='red',marker='<',label=label1txt)
2327
-
2328
- footnote1="\n"+"无风险利率"+"(100bp = 1%)-->"
2329
- footnote2="\n"+"债券票面利率"+str(round(k1*100,2))+"%,违约回收率"+str(round(theta*100))+"%,"
2330
- footnote3="无风险利率初始值"+str(round(i1*100,2))+'%'
2331
- footnote=footnote1+footnote2+footnote3
2332
-
2333
- ax.set_xlabel(footnote,fontsize=xlabel_txt_size)
2334
- ax.set_ylabel(label1txt,fontsize=ylabel_txt_size)
2335
- ax.legend(loc=loc1,fontsize=legend_txt_size)
2336
-
2337
- ax2 = ax.twinx()
2338
-
2339
- #设置第2纵轴的刻度范围,以便当第2条曲线与第1条曲线重合时能够区分开
2340
- ax2ymin=df['elr'].min()
2341
- ax2ymax=df['elr'].max()
2342
- ax2ymax=ax2ymax * 1.05
2343
- ax2.set_ylim([ax2ymin, ax2ymax])
2344
-
2345
- label2txt='预期损失率%'
2346
- ax2.plot(df['i1'],df['elr'],color='green',marker='>',label=label2txt)
2347
-
2348
- ax2.set_ylabel(label2txt,fontsize=ylabel_txt_size)
2349
- ax2.legend(loc=loc2,fontsize=legend_txt_size)
2350
-
2351
- titletxt="KPMG风险中性定价:无风险利率对债券违约估计的影响"
2352
- plt.title(titletxt,fontsize=title_txt_size,fontweight='bold')
2353
-
2354
- plt.xticks(rotation=30)
2355
-
2356
- plt.gca().set_facecolor('whitesmoke')
2357
- plt.show(); plt.close()
2358
-
2359
- return df
2360
-
2361
- if __name__ == "__main__":
2362
- df=kpmg_rnpm1_rf(k1,theta,i1,loc1='upper center',loc2='lower center')
2363
- #==============================================================================
2364
- if __name__=='__main__':
2365
- k1=0.15
2366
- theta=0.8
2367
- i1=0.05
2368
- thetalist=[-30,-25,-20,-15,-10,-5,5,10,15,20,25,30]
2369
- loc1='upper left'
2370
- loc2='lower right'
2371
-
2372
- def kpmg_rnpm1_rrd(k1,theta,i1, \
2373
- thetalist=[-100,-50,-30,-20,-15,-10,-5,5,10,15,20,30,50,100], \
2374
- loc1='best',loc2='best'):
2375
- """
2376
- 功能:展示KPMG风险中性定价债券的违约回收率对于债券违约概率pd=(1-P1)和预期损失率ELR的影响
2377
- k1:票面利率coupon rate,注意不能低于rf
2378
- theta:违约时的回收率recovery rate at default。loss given default lgd=1-theta
2379
- theta为零时表示违约时无可回收的资产,最小为零,最大为小于100%,不能超出此范围
2380
- i1:无风险收益率risk-free rate,注意不能高于cr,一般也不能低于零
2381
- thetalist:以1%为单位围绕违约回收率theta的变化值,不包括theta本身
2382
- """
2383
-
2384
- #生成ilist
2385
-
2386
- #生成EDR和ELR
2387
- import pandas as pd
2388
- df=pd.DataFrame(columns=('k1','theta','changeInPct','i1','pd','elr'))
2389
-
2390
- pdt,elr=calc_kpmg_rnpm1(k1,theta,i1)
2391
- xl=str(round(theta*100))+'%'
2392
- s=pd.Series({'k1':k1,'theta':xl,'i1':i1,'changeInPct':0,'pd':pdt*100,'elr':elr*100})
2393
- try:
2394
- df=df.append(s, ignore_index=True)
2395
- except:
2396
- df=df._append(s, ignore_index=True)
2397
-
2398
-
2399
- #计算theta变化对于债券pd和elr的影响
2400
- for i in thetalist:
2401
- t1t=round(theta + i/100.0,4)
2402
- if (t1t >= 1) | (t1t <0): continue
2403
-
2404
- pdt,elr=calc_kpmg_rnpm1(k1,t1t,i1)
2405
- if pdt >= 1:
2406
- continue
2407
-
2408
- xl=str(round(t1t*100))+'%'
2409
-
2410
- s=pd.Series({'k1':k1,'theta':xl,'i1':i1,'changeInPct':i,'pd':pdt*100,'elr':elr*100})
2411
- try:
2412
- df=df.append(s, ignore_index=True)
2413
- except:
2414
- df=df._append(s, ignore_index=True)
2415
-
2416
- #按照到期收益率升序排序
2417
- df.sort_values(by=['changeInPct'],ascending=[True],inplace=True)
2418
- #指定索引
2419
- df.reset_index(drop=True,inplace=True)
2420
-
2421
- #绘图
2422
- #fig = plt.figure(figsize=(12.8,7.2),dpi=300)
2423
- fig = plt.figure(figsize=(12.8,6.4),dpi=300)
2424
- #plt.rcParams['figure.dpi']=300
2425
-
2426
- ax = fig.add_subplot(111)
2427
- label1txt='违约概率%'
2428
- ax.plot(df['theta'],df['pd'],color='red',marker='<',label=label1txt)
2429
-
2430
- footnote1="\n"+"违约回收率 -->"
2431
- footnote2="\n"+"债券票面利率"+str(round(k1*100,2))+"%,违约回收率初始值"+str(round(theta*100))+"%,"
2432
- footnote3="无风险利率"+str(round(i1*100,2))+'%'
2433
- footnote=footnote1+footnote2+footnote3
2434
-
2435
- ax.set_xlabel(footnote,fontsize=xlabel_txt_size)
2436
- ax.set_ylabel(label1txt,fontsize=ylabel_txt_size)
2437
- ax.legend(loc=loc1,fontsize=legend_txt_size)
2438
-
2439
- ax2 = ax.twinx()
2440
- label2txt='预期损失率%'
2441
- ax2.plot(df['theta'],df['elr'],color='green',marker='>',label=label2txt)
2442
-
2443
- ax2.set_ylabel(label2txt,fontsize=ylabel_txt_size)
2444
- ax2.legend(loc=loc2,fontsize=legend_txt_size)
2445
-
2446
- titletxt="KPMG风险中性定价:违约回收率对债券违约估计的影响"
2447
- plt.title(titletxt,fontsize=title_txt_size,fontweight='bold')
2448
-
2449
- plt.xticks(rotation=30)
2450
-
2451
- plt.gca().set_facecolor('whitesmoke')
2452
- plt.show(); plt.close()
2453
-
2454
- return df
2455
-
2456
- if __name__ == "__main__":
2457
- df=kpmg_rnpm1_rrd(k1,theta,i1,loc1='upper left',loc2='lower right')
2458
-
2459
- theta_sample=[-30,-25,-20,-15,-10,-5,5,10,15,20,25,30]
2460
- df=kpmg_rnpm1(k1,theta,i1,demo='theta',sample=theta_sample, \
2461
- loc1='upper left', \
2462
- loc2='lower right')
2463
-
2464
- #==============================================================================
2465
- if __name__=='__main__':
2466
- k1=0.15
2467
- theta=0.8
2468
- i1=0.05
2469
- demo='k1'
2470
- sample=[-100,-50,-30,-20,-15,-10,-5,5,10,15,20,30,50,100]
2471
- loc1='upper left'
2472
- loc2='lower right'
2473
-
2474
- def kpmg_rnpm1(k1,theta,i1,demo='k1', \
2475
- sample='default', \
2476
- loc1='best',loc2='best'):
2477
- """
2478
- 功能:展示KPMG风险中性定价各个因素对于一年期债券违约概率pd=(1-P1)和预期损失率ELR的影响
2479
- k1:票面利率coupon rate,注意不能低于rf
2480
- theta:违约时的回收率recovery rate at default。loss given default lgd=1-theta
2481
- theta为零时表示违约时无可回收的资产,最小为零,最大为100%,不能超出此范围
2482
- i1:无风险收益率risk-free rate,注意不能高于cr,一般也不能低于零
2483
- demo: k1为演示票面利率的影响,theta为演示违约回收率的影响,i1为演示无风险利率的影响
2484
- sample:各个影响因素展示的样本值,default为默认,可以自行指定列表
2485
- """
2486
-
2487
- #检查demo的类型
2488
- demolist=['k1','theta','i1']
2489
- if not (demo in demolist):
2490
- print(" #Error(kpmg_rnpm1): unsupported demo type",demo)
2491
- print(" Supported demo types:")
2492
- print(" k1 - demo the impact of coupon interest rate")
2493
- print(" theta - demo the impact of recovery rate at default")
2494
- print(" i1 - demo the impact of risk-free interest rate")
2495
- return None
2496
-
2497
- #演示票面利率的影响
2498
- if demo == 'k1':
2499
- if sample == 'default':
2500
- df=kpmg_rnpm1_cr(k1,theta,i1,loc1=loc1,loc2=loc2)
2501
- else:
2502
- df=kpmg_rnpm1_cr(k1,theta,i1,k1list=sample, \
2503
- loc1=loc1,loc2=loc2)
2504
-
2505
- #演示无风险利率的影响
2506
- if demo == 'i1':
2507
- if sample == 'default':
2508
- df=kpmg_rnpm1_rf(k1,theta,i1,loc1=loc1,loc2=loc2)
2509
- else:
2510
- df=kpmg_rnpm1_rf(k1,theta,i1,i1list=sample, \
2511
- loc1=loc1,loc2=loc2)
2512
-
2513
- #演示违约回收率的影响
2514
- if demo == 'theta':
2515
- if sample == 'default':
2516
- df=kpmg_rnpm1_rrd(k1,theta,i1,loc1=loc1,loc2=loc2)
2517
- else:
2518
- df=kpmg_rnpm1_rrd(k1,theta,i1,thetalist=sample, \
2519
- loc1=loc1,loc2=loc2)
2520
-
2521
- return df
2522
-
2523
- if __name__=='__main__':
2524
- df=kpmg_rnpm1(k1,theta,i1,demo='k1',loc1='center left',loc2='center right')
2525
- df=kpmg_rnpm1(k1,theta,i1,demo='i1')
2526
- df=kpmg_rnpm1(k1,theta,i1,demo='theta')
2527
-
2528
- #===============================================================================================
2529
- def get_tbond_yield():
2530
- """
2531
- 功能:获取中美国债收益率数据
2532
- """
2533
-
2534
- import akshare as ak
2535
- df=ak.bond_zh_us_rate()
2536
-
2537
- # 截取样本
2538
- import pandas as pd
2539
- df['date']=pd.to_datetime(df['日期'])
2540
- df.set_index('date',inplace=True)
2541
-
2542
- return df
2543
-
2544
- #===============================================================================================
2545
- if __name__ =="__main__":
2546
-
2547
- df=get_tbond_yield()
2548
- # 新冠疫情三年
2549
- start='2020-2-1'; end='2022-12-20'
2550
-
2551
- tblist=[2,5,10,30]
2552
- country=['China','USA']
2553
- df=compare_tbond_yield(start,end,tblist,country)
2554
-
2555
- tblist=[2,5,10,30]
2556
- country=['USA','China']
2557
- df=compare_tbond_yield(start,end,tblist,country)
2558
-
2559
- tblist=[2,30]
2560
- country=['USA','China']
2561
- df=compare_tbond_yield(start,end,tblist,country)
2562
-
2563
- tblist=[2]
2564
- country=['China','USA']
2565
- df=compare_tbond_yield(start,end,tblist,country)
2566
-
2567
- def compare_tbond_yield(df,start,end,tblist=['2','5','10','30'],country=['China','USA'],tb10_2=False,graph=True):
2568
- """
2569
- 功能:绘制和比较国债收益率曲线
2570
- start,end:日期期间
2571
- tblist:国债期限年数,若不少于2个,以此为主,后面的country只取第1项。默认[2,5,10,30,'GDP rate']
2572
- country:国家,若tblist只有一项,则取['China','USA'];否则自取第1项
2573
- """
2574
- # 检查日期的合理性
2575
- result,startpd,endpd=check_period(start,end)
2576
- if not result:
2577
- print(" #Error(compare_tbond_yield): invalid date period",start,end)
2578
- return None
2579
-
2580
- # 将tblist和country转化为列表
2581
- if isinstance(tblist,str):
2582
- tblist1=[tblist]
2583
- elif isinstance(tblist,int):
2584
- tblist1=[str(tblist)]
2585
- elif isinstance(tblist,list):
2586
- tblist1=tblist
2587
- else:
2588
- print(" #Error(compare_tbond_yield): invalid treasury bond maturity list in",tblist)
2589
- return None
2590
-
2591
- tbtmp=[]
2592
- for tb in tblist1:
2593
- if isinstance(tb,int):
2594
- tbtmp=tbtmp+[str(tb)]
2595
- else:
2596
- tbtmp=tbtmp+[tb]
2597
- tblist2=tbtmp
2598
-
2599
- if isinstance(country,str):
2600
- country1=[country]
2601
- elif isinstance(country,list):
2602
- country1=country
2603
- else:
2604
- print(" #Error(compare_tbond_yield): invalid country list in",country)
2605
- return None
2606
-
2607
- # 支持的国家
2608
- for c in country1:
2609
- if not (c in ['China', 'USA']):
2610
- print(" #Error(compare_tbond_yield): invalid country list in",country)
2611
- print(" Supported countries are",['China', 'USA'])
2612
- return None
2613
-
2614
- # 判断国债单年数还是多年数
2615
- mode='mt1c'
2616
- if len(tblist2) >=2:
2617
- mode='mt1c' #国债多年数,单一国家
2618
- tblist3=tblist2
2619
- country3=[country1[0]]
2620
- else:
2621
- mode='1tmc' #国债单年数,双国家
2622
- tblist3=tblist2
2623
- country3=country1
2624
-
2625
- # 确定绘图字段
2626
- collist=[]
2627
- if mode == 'mt1c':
2628
- if country3[0].lower() == 'china':
2629
- ctext='中国'
2630
- else:
2631
- ctext='美国'
2632
-
2633
- for y in tblist3:
2634
- collist=collist+[ctext+'国债收益率'+str(y)+'年']
2635
-
2636
- if tb10_2:
2637
- collist=[ctext+'国债收益率10年-2年']
2638
-
2639
- if mode == '1tmc':
2640
- countrylist=[]
2641
- for c in country3:
2642
- if c.lower() == 'china':
2643
- ctext='中国'
2644
- else:
2645
- ctext='美国'
2646
- countrylist=countrylist+[ctext]
2647
-
2648
- for c in countrylist:
2649
-
2650
- if not tb10_2:
2651
- collist=collist+[c+'国债收益率'+tblist3[0]+'年']
2652
- else:
2653
- collist=collist+[c+'国债收益率10年-2年']
2654
-
2655
- # 选取数据
2656
- df1=df[(df.index >= startpd) & (df.index <= endpd)]
2657
-
2658
- # 绘图
2659
- if graph:
2660
- df2=df1[collist]
2661
- df2.dropna(inplace=True)
2662
-
2663
- if mode == 'mt1c':
2664
- title_txt=ctext+"不同期限国债的收益率对比"
2665
- if tb10_2:
2666
- title_txt=ctext+"10年期与2年期国债收益率之差"
2667
-
2668
- else:
2669
- title_txt="中美同期限国债的收益率对比"
2670
- if tb10_2:
2671
- title_txt="中美10年期与2年期国债收益率之差对比"
2672
-
2673
- y_label='收益率%'
2674
-
2675
- notes=[]
2676
- for c in list(df2):
2677
- tbmax=df2[c].max()
2678
- tbmin=df2[c].min()
2679
- mmtext=c+':最高'+str(tbmax)+'%,最低'+str(tbmin)+'%'
2680
- notes=notes+[mmtext]
2681
-
2682
- import datetime; today=datetime.date.today()
2683
- footnote1="\n数据来源:东方财富,"+str(today)+'统计\n'
2684
-
2685
- footnote2=''
2686
- if tb10_2:
2687
- footnote2=notes[0]+'\n'
2688
-
2689
- #footnote3='国债收益率为内部/到期收益率,一般不低于银行同期限存款利率'
2690
- footnote3=''
2691
-
2692
- footnote=footnote1+footnote2+footnote3
2693
-
2694
- axhline_label=''
2695
- if tb10_2:
2696
- axhline_label='零分界线'
2697
-
2698
- draw_lines(df2,y_label,x_label=footnote, \
2699
- axhline_value=0,axhline_label=axhline_label, \
2700
- title_txt=title_txt, \
2701
- data_label=False,resample_freq='D',smooth=True)
2702
- """
2703
- if not tb10_2:
2704
- numOfCols=len(df2)
2705
- if numOfCols >= 2:
2706
- numberPerLine=2
2707
- else:
2708
- numberPerLine=1
2709
- printInLine_md(aList=notes,numberPerLine=numberPerLine,colalign='left')
2710
- """
2711
- return df1
2712
-
2713
- #===============================================================================================
2714
- #==============================================================================
2715
- def bond_malkiel(coupon_rate,maturity_years,ytm,coupon_times=2,par_value=100, \
2716
- #rate_change_list=[-100,-50,-20,-10,-5,5,10,20,50,100], \
2717
- rate_change_list=[-300,-250,-200,-150,-100,-50,50,100,150,200,250,300], \
2718
- maturity_years_list= [1, 2, 3, 5, 10, 15, 20, 30], \
2719
- coupon_rate_list=[-300,-250,-200,-150,-100,-50,50,100,150,200,250,300], \
2720
- model='malkiel1'):
2721
- """
2722
- 套壳函数:bond_malkiel1/2/3/4/5
2723
- """
2724
-
2725
- # 检查模型
2726
- malkiel_models=['malkiel1','malkiel2','malkiel3','malkiel4','malkiel5']
2727
- if model not in malkiel_models:
2728
- print(" #Error(bond_malkiel): invalid Malkiel model",model)
2729
- print(" Supported Malkiel models:")
2730
- print(" ",malkiel_models)
2731
-
2732
- return
2733
-
2734
- if model=='malkiel1':
2735
- bond_malkiel1(aytm=ytm,yper=maturity_years,c=coupon_rate,fv=par_value, \
2736
- mterm=coupon_times,bplist=rate_change_list)
2737
- return
2738
-
2739
- if model=='malkiel2':
2740
- bond_malkiel2(aytm=ytm,yper=maturity_years,c=coupon_rate,fv=par_value, \
2741
- mterm=coupon_times,yperlist=maturity_years_list)
2742
- return
2743
-
2744
-
2745
- if model=='malkiel3':
2746
- bond_malkiel3(aytm=ytm,yper=maturity_years,c=coupon_rate,fv=par_value, \
2747
- mterm=coupon_times)
2748
- return
2749
-
2750
-
2751
- if model=='malkiel4':
2752
- bond_malkiel4(aytm=ytm,yper=maturity_years,c=coupon_rate,fv=par_value, \
2753
- mterm=coupon_times, \
2754
- bplist=rate_change_list)
2755
- """
2756
- bond_malkiel4(aytm=ytm,yper=maturity_years,c=coupon_rate,fv=par_value, \
2757
- mterm=coupon_times, \
2758
- bplist=[-300,-250,-200,-150,-100,-50,50,100,150,200,250,300])
2759
- """
2760
- return
2761
-
2762
-
2763
- if model=='malkiel5':
2764
- bond_malkiel5(aytm=ytm,yper=maturity_years,c=coupon_rate,fv=par_value, \
2765
- mterm=coupon_times, \
2766
- clist=coupon_rate_list)
2767
- return
2768
-
2769
-
2770
- #==============================================================================
2771
- if __name__ =="__main__":
2772
- start="MRY"; end="today"
2773
- term="1Y"
2774
- term=["1Y","5Y","10Y"]
2775
-
2776
- power=1
2777
- average_value=True
2778
- loc1="best"; loc2="best"
2779
- mark_top=False; mark_bottom=False; mark_end=False
2780
- twinx=False
2781
-
2782
- annotate=True; annotate_value=True
2783
- facecolor="papayawhip"
2784
-
2785
- rates=treasury_yield_trend_china(term="1Y")
2786
- rates=treasury_yield_trend_china(term=["1Y","5Y","10Y"])
2787
-
2788
- def treasury_trend_china(term="1Y",start="MRY",end="today", \
2789
- power=0, \
2790
- average_value=False, \
2791
- mark_top=False,mark_bottom=False,mark_end=False, \
2792
- annotate=True,annotate_value=True, \
2793
- twinx=False, \
2794
- loc1="best",loc2="best", \
2795
- facecolor="papayawhip"):
2796
- """
2797
-
2798
- 功能:分析中国国债收益率走势,支持单个期限或多个期限进行对比
2799
- """
2800
- #检查国债期限
2801
- term_list=['3M','6M','1Y','3Y','5Y','7Y','10Y','30Y']
2802
- term_list_names=['3个月','6个月','1年期','3年期','5年期','7年期','10年期','30年期']
2803
-
2804
- start1,end1=start_end_preprocess(start,end)
2805
- #期间不能超过一年
2806
- import pandas as pd
2807
- date1=pd.to_datetime(start1)
2808
- date2=pd.to_datetime(end1)
2809
- days=days_between_dates(date1, date2)
2810
- if days>=365:
2811
- days=365
2812
- start1=date_adjust(end1, adjust=-days)
2813
-
2814
- if isinstance(term,str):
2815
- if not term in term_list:
2816
- print(" #Warning(treasury_trend_china): unsupported treasury term",term)
2817
- print(" Supported terms:", end='')
2818
- print_list(term_list,leading_blanks=1,end='\n')
2819
- return None
2820
- termlist=[term]
2821
- elif isinstance(term,list):
2822
- for t in term:
2823
- if not t in term_list:
2824
- print(" #Warning(treasury_trend_china): unsupported treasury term",t)
2825
- term.remove(t)
2826
- termlist=term
2827
- else:
2828
- print(" #Warning(treasury_trend_china):",term," is unsupported")
2829
- print(" Supported terms:", end='')
2830
- print_list(term_list,leading_blanks=1,end='\n')
2831
- return None
2832
-
2833
- print(" Searching treasury information in China ...")
2834
- df=pd.DataFrame()
2835
- for t in termlist:
2836
- if len(termlist) > 1:
2837
- print_progress_percent2(t,termlist,steps=len(termlist),leading_blanks=4)
2838
-
2839
- dftmp=treasury_yields_china(start1,end1,term=t)
2840
- dftmp[t]=dftmp['rate']*100 #转换为百分数
2841
- dftmp1=pd.DataFrame(dftmp[t])
2842
-
2843
- if len(df)==0:
2844
- df=dftmp1
2845
- else:
2846
- df=pd.merge(df,dftmp1,left_index=True,right_index=True)
2847
-
2848
- #绘图
2849
- titletxt=text_lang("中国国债收益率走势","China Treasury Yield Trend")
2850
- ylabeltxt=text_lang('收益率%',"Yield%")
2851
- import datetime; todaydt = datetime.date.today()
2852
- footnote=text_lang("数据来源:中国债券信息网,","Data source: China Bond Info, ")+str(todaydt)
2853
-
2854
- if len(termlist)==1: #单曲线
2855
- pos=term_list.index(termlist[0])
2856
- termname=term_list_names[pos]
2857
- ylabeltxt=text_lang(termname+ylabeltxt,termlist[0]+' '+ylabeltxt)
2858
-
2859
- plot_line(df,colname=termlist[0],collabel=termlist[0], \
2860
- ylabeltxt=ylabeltxt,titletxt=titletxt,footnote=footnote, \
2861
- power=power,average_value=average_value, \
2862
- loc=loc1, \
2863
- mark_top=mark_top,mark_bottom=mark_bottom,mark_end=mark_end, \
2864
- facecolor=facecolor)
2865
-
2866
- if len(termlist)==2: #两条曲线,twinx
2867
- bond1=termlist[0]; pos1=term_list.index(bond1); bond1name=term_list_names[pos1]
2868
- bond2=termlist[1]; pos2=term_list.index(bond2); bond2name=term_list_names[pos2]
2869
-
2870
- df1=pd.DataFrame(df[bond1]); ticker1=''; colname1=bond1; label1=bond1name
2871
- df2=pd.DataFrame(df[bond2]); ticker2=''; colname2=bond2; label2=bond2name
2872
-
2873
- plot_line2(df1,ticker1,colname1,label1, \
2874
- df2,ticker2,colname2,label2, \
2875
- ylabeltxt=ylabeltxt,titletxt=titletxt,footnote=footnote, \
2876
- power=power, \
2877
- twinx=twinx, \
2878
- loc1=loc1,loc2=loc2, \
2879
- color1='red',color2='blue',facecolor=facecolor)
2880
-
2881
- if len(termlist) > 2: #多条曲线
2882
- df['date']=df.index
2883
- df.set_index("date",inplace=True)
2884
-
2885
- for t in termlist:
2886
- tpos=term_list.index(t); tname=term_list_names[tpos]
2887
- df.rename(columns={t:tname},inplace=True)
2888
-
2889
- draw_lines(df,y_label=ylabeltxt,x_label=footnote, \
2890
- axhline_value=0,axhline_label='', \
2891
- title_txt=titletxt, \
2892
- data_label=False, \
2893
- loc=loc1,annotate=annotate,annotate_value=annotate_value, \
2894
- mark_top=mark_top,mark_bottom=mark_bottom,mark_end=mark_end, \
2895
- facecolor=facecolor)
2896
-
2897
- return df
2898
-
2899
-
2900
- #==============================================================================