当前位置:实例文章 » Python实例» [文章]Python—看我分析下已经退市的 可转债 都有什么特点

Python—看我分析下已经退市的 可转债 都有什么特点

发布人:shili8 发布时间:2023-03-09 09:06 阅读次数:19

分析

需求分析

  • 可转债退市原因的种类与占比是多少

  • 强赎非强赎导致的退市可转债 存续时间 维度占比

  • 强赎非强赎导致的退市可转债 发行资金 规模占比

  • 强赎非强赎导致的退市可转债 各个评级 的占比

  • 强赎非强赎导致的退市可转债 各个行业(一级行业) 的占比

程序环境分析

  • 需要依赖的三方代码库:

    • requests(处理http请求)

    • pyecharts(图表显示)

    • pandas(数据表处理)

    • BeautifulSoup(html文件解析)

  • 抓取数据的网站

    • 集思录(www.jisilu.cn/)

程序逻辑分析

  • 通过 http请求 抓取已退市可转债的列表

  • 获取列表中的每个转债代码,通过 http请求 访问转债详情页面,通过BeautifulSoup库提供的方法 获取到债券评级与行业

  • 获取列表中的(转债代码、转债名称、发行规模、存续年限、退市原因、债券评级、行业)字段,存入到csv文件中

    • 存入到csv文件是因为后面要做各个维度的数据分析,如果不存到文件中,后面数据分析中一但出现一点错误,就又得重新去网站抓取数据,比较麻烦

  • 读取csv文件中的数据,用pandas转成表格,按退市原因分组计数,用pyecharts做成饼图展示

  • 读取csv文件中的数据,用pandas转成表格,按强赎与非强赎中的存续时间分组计数,用pyecharts做成饼图

  • 读取csv文件中的数据,用pandas转成表格,按强赎与非强赎中的发行资金规模分组计数(从大到小排序,取前15),用pyecharts做成柱形图

  • 读取csv文件中的数据,用pandas转成表格,按强赎与非强赎中的债券评级分组计数,用pyecharts做成饼图

  • 读取csv文件中的数据,用pandas转成表格,按强赎与非强赎中的债券行业分组计数,用pyecharts做成柱状图

实现代码

编写http请求方法

import pandas as pd
from bs4 import BeautifulSoup
from pyecharts.charts import Bar, Pie
from pyecharts import options as opts


def get_request(url):
    # 设置请求头,防止部分网站对请求头做拦截 
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36"
    }
    try:
        resp = requests.get(url, headers=headers)
        resp.encoding="utf-8"
        if resp.status_code == 200:
            return resp.text
    except Exception as e:
        print("http请求出错:",e)
        return None
 

爬取债券详情页,取 债券评级与行业 (列表页面中没有这两个)

def assemble_grade(resp):
    resp_json=json.loads(resp)
    datas=resp_json["rows"]
    if datas is None:
        return None

    dataList=[]
    for data in datas:
        # 转债代码、转债名称、发行规模、存续年限、退市原因、债券评级
        bond_id=data["cell"]["bond_id"]
        bond_nm=data["cell"]["bond_nm"]
        orig_iss_amt=data["cell"]["orig_iss_amt"]
        listed_years=data["cell"]["listed_years"]
        delist_notes=data["cell"]["delist_notes"]

        #获取详情
        grade,indusity=parse_html("https://www.jisilu.cn/data/convert_bond_detail/%s" %bond_id)
        print("当前可转债是:%s,评级是:%s,行业是:%s" %(bond_nm,grade,indusity))

        dataList.append(",".join([bond_id,bond_nm,orig_iss_amt,listed_years,delist_notes,grade,indusity]))

        #防止访问过快,网站拦截,睡5秒
        time.sleep(2)

    return dataList
 

将抓取的数据存入csv文件中

def write_csv(data):
    if not data:
        print("当前要写入的数据为空")
    with open("bonds.csv", "w", encoding="utf-8") as f:
        f.write("\n".join(data))
 

读取csv文件的数据,用做分析

'''
type 1全部  2强赎  3非强赎
'''
def read_csv(type):
    dataList=[]
    with open("bonds.csv", "r", encoding="utf-8") as f:
        lines=f.readlines()
        for line in lines:
            line=line.replace("\\n","")
            data=line.split(",")

            # 排除可交换债
            if data[1].endswith("EB"):
                continue

            industry=data[6].split("-")[0]
            year=assemble_year(data[3])

            if type==1:
                dataList.append([data[2], year, data[4],data[5],industry])
            elif type==2:
                if data[4]=="强赎":
                    dataList.append([data[2], year, data[4],data[5],industry])
            elif type==3:
                if data[4] != "强赎":
                    dataList.append([data[2],year,data[4],data[5],industry])
    return dataList
 

按退市原因进行分析,生成饼图

def craete_notes_pie(pf):
    data = pf.groupby(by=["delist_notes"]).size()
    notesList=list(data.index)
    notesCount=list(data)

    c=(
        Pie()
        .add("",[list(z) for z in zip(notesList,notesCount)])
        .set_global_opts(title_opts=opts.TitleOpts(title="退市原因统计"))
        .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{c}个  ,占比:{d}%"))
        .render("notes.html")
    )

按强赎与非强赎中的存续时间分组计数,用pyecharts做成饼图

def craete_years_pie(pf,name,title):
    data = pf.groupby(by=["listed_years"]).size()
    notesList = list(data.index)
    notesCount = list(data)

    c = (
        Pie()
            .add("", [list(z) for z in zip(notesList, notesCount)])
            .set_global_opts(title_opts=opts.TitleOpts(title=title),legend_opts=opts.LegendOpts(pos_left="20%"))
            .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{c}个  ,占比:{d}%"))
            .render(name)
    )
 

强赎与非强赎中的发行资金规模分组计数(从大到小排序,取前15),用pyecharts做成柱形图

def craete_amt_bar(pf,name,title):
    data = pf.groupby(by=["orig_iss_amt"]).size().reset_index(name="size").sort_values("size",ascending=False).head(15)
    print(data)
    notesList=list(data["orig_iss_amt"])
    notesCount=list(data["size"])

    c=(
        Bar()
        .add_xaxis(notesList)
        .add_yaxis("发行规模",notesCount)
        .set_global_opts(title_opts=opts.TitleOpts(title=title),xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-20)))
        .render(name)
    )
 

按强赎与非强赎中的债券评级分组计数,用pyecharts做成饼图

def craete_grade_pie(pf,name,title):
    data = pf.groupby(by=["grade"]).size()
    notesList=list(data.index)
    notesCount=list(data)

    c=(
        Pie()
        .add("",[list(z) for z in zip(notesList,notesCount)])
        .set_global_opts(title_opts=opts.TitleOpts(title=title))
        .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}:{c}个  ,占比:{d}%"))
        .render(name)
    )
 

按强赎与非强赎中的债券行业分组计数,用pyecharts做成柱状图

def craete_industry_bar(pf,name,title):
    # 按行业分组,排序,取前30位
    data = pf.groupby(by=["industry"]).size().reset_index(name="size").sort_values("size",ascending=False).head(30)
    print(data)
    notesList=list(data["industry"])
    notesCount=list(data["size"])

    c=(
        Bar()
        .add_xaxis(notesList)
        .add_yaxis("行业",notesCount)
        .set_global_opts(title_opts=opts.TitleOpts(title=title),xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45)))
        .render(name)
    )
 

最终调用 主方法

def main():
    #生成动太时间戳
    rTime=str(round(time.time()*1000))
    #获取退市可转债列表
    resp=get_request("https://www.jisilu.cn/data/cbnew/delisted/ ___jsl=LST___t="+rTime)

    #组装每个可转债的评级
    dataList=assemble_grade(resp)

    #将数据写入csv
    write_csv(dataList)

    #读取csv文件中的数据,并制成表格(发行规模,存续时间,退市原因)
    dfData = read_csv(1)
    pf = pd.DataFrame(dfData, columns=["orig_iss_amt", "listed_years", "delist_notes", "grade", "industry"])

    dfData=read_csv(2)
    pf2=pd.DataFrame(dfData, columns=["orig_iss_amt", "listed_years", "delist_notes","grade","industry"])

    dfData = read_csv(3)
    pf3 = pd.DataFrame(dfData, columns=["orig_iss_amt", "listed_years", "delist_notes", "grade","industry"])

    #按退市原因分组计数,用pyecharts做成饼图
    craete_notes_pie(pf)

    #存续时间维度占比
    craete_years_pie(pf2,"qs_years.html","强赎存续年限统计")
    craete_years_pie(pf3,"years.html","非强赎存续年限统计")

    # 强赎与非强赎发行资金规模占比
    craete_amt_bar(pf2,"qs_amt.html","强赎发行规模统计")
    craete_amt_bar(pf3, "amt.html","非强赎发行规模统计")

    # 强赎与非强赎各个评级的占比
    craete_grade_pie(pf,"qs_grade.html","强赎评级统计")
    craete_grade_pie(pf, "grade.html", "非强赎评级统计")

    # 强赎与非强赎行业占比
    craete_industry_bar(pf2,"qs_industry.html","强赎行业统计")
    craete_industry_bar(pf3, "industry.html", "非强赎行业统计")

if __name__ == '__main__':
    main()
 

结果展示图

退市原因分析:总135个已退市可转债中,127个都是 强赎导致的,占比是94.08%;8个是因为到期或者资产不足导致,占比5.92%

存续时间分析:

强赎的可转债中,按存续时间分析,1年以内被强赎的最多,有53个,占比 41.73%,其次是 大于1年,小于等于2年的,有46个,占比 36.22%, 其次是 大于2年,小于等于3年的,有 14个,占比11.02% .... ,从分析结果中可得知 可转债发行后,短时间内被赎回的概率比较大存续时间越长,赎回概率则越小

发行规模分析:

强赎的可转债中,按资金规模划分 发行10亿的有6个,4.2亿的3个,25亿的3个...

按债券评级分析:

强赎的可转债中,按评级划分 占比最多的是AA级,有61个,占比 45.18%,其次是 AA+ 有25个,占比18.52, 第三是 AAA有22个,占比16.3%...

总结:最好买AA- 级或以上的,被强赎的概率最高

按债券行业分析:

强赎的可转债中,按一级行业划分,取排名靠前的30个,占比最多的是 电子行业,其次是医药生物、化工、机械设备

总结:

  • 岂今为止,在已退市的可转债中,94%以上都是被强赎的

  • 可转债发行后,1年以内被强赎的概率最高,存续时间越往后,则概率越低

  • 从债券评级上看,AA- 或以上,被强赎的概率最高,AAA级债券暂还没出现被回售的

  • 从行业上看,电气、医药生物、化工、机械设备、电气设备等都是被强赎比较高的行业

注:此历史数据只供参考,具体投资还需要独立思考,且近两年,可转债的发行数量增加很多,质量更是参差不齐,选择时还需要谨慎。 如果能抱着持有到期的心态,买上价格在100以内AA级及以上的债券,相信亏本的可能性及小,且收益不会太差,此乃保守投资者投资的一种方式

程序猿与投资生活实录已改名为 程序猿知秋,WX同款,欢迎关注!

相关标签:

免责声明

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱290110527@qq.com删除。

其他信息

其他资源

Top