财新传媒 财新传媒

阅读:0
听报道
【风险提示】
本文原始数据来自于WIND信用债板块的收盘价,对于市场成交不活跃的个券,可能与实际市场表现不完全符合。
2020年11月10日,20永煤SCP003违约,对信用债市场的情绪和流动性带来负面冲击,目前冲击仍在继续。我们希望可以及时监测信用债异常成交价格的情况。使用WIND的Python接口监测信用债异常成交的步骤如下:
 
1 获得所有信用债的收盘价(以MTN为例)
 
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from WindPy import w
 
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示正负号
我们先通过WIND接口获得MTN的所有wind代码,因为MTN的全部个券有6200多只,所以我们要分开获取收盘价。
w.start()
_code = w.wset("sectorconstituent", "date=2020-11-16;sectorid=1000004570000000")
# 获取MTN的所有代码
 
_code1 = ','.join(_code.Data[1][0:5000])
_code2 = ','.join(_code.Data[1][5000:])
# 将代码分拆合并
 
_data1 = w.wsd(_code1, "close", "2020-11-13", "2020-11-16", "")
_data1 = pd.DataFrame(_data1.Data, index=_data1.Codes, columns=[x.strftime('%Y-%m-%d') for x in _data1.Times])
 
_data2 = w.wsd(_code2, "close", "2020-11-13", "2020-11-16", "")
_data2 = pd.DataFrame(_data2.Data, index=_data2.Codes, columns=[x.strftime('%Y-%m-%d') for x in _data2.Times])
# 获得MTN的所有券在2020-11-13日与2020-11-16日的成交价
_data = pd.concat([_data1, _data2])
最终获得数据大概如下:
2 提取异常成交的个券
 
比如我们设定,成交价跌幅超过0.5元的,就认为是异常成交,并按照跌幅排序
_data['diff'] = _data['2020-11-16'] - _data['2020-11-13']
_abnormal = _data[_data['diff'] < -0.5]
_abnormal = _abnormal.sort_values('diff')
从下图可以看到,我们从6289只MTN中,筛选出98只跌幅超过0.5元的个券,其中最大的跌幅是-14.5元。
 
3 获得异常成交个券的详细信息
 
我们已经获得了异常成交个券,我们需要了解更多的信息,比如个券的名称,存续债券规模,到期日,地区,行业,是否是城投,甚至各种财务指标等。我们就用个券的wind代码,来匹配各项信息。
_data = w.wss(','.join(_abnormal.index), "sec_name,issuerupdated,outstandingbalance,maturitydate,term,repo_lastestdate,abs_industry,abs_industry1,abs_province,municipalbond", "tradeDate=2020-11-16")
_data = pd.DataFrame(np.array(_data.Data).T, columns=_data.Fields, index=_data.Codes)
_data = _abnormal.join(_data)
下图中的信息,可以根据自己需求,进行添加或者删改
我们也可以比较方便的做一个统计分析,比如我们来看看,异常成交的MTN个券的企业性质和地区分布如下。从下图中可以看到,地方国有企业的异常下跌券数,是明显高于其他企业性质的。
 
代码如下:
fig = plt.figure(figsize=(16,8))
ax1 = fig.add_subplot(121)
_data['ABS_INDUSTRY1'].hist(ax=ax1, grid=False, xrot=90)
ax2 = fig.add_subplot(122)
_data['ABS_PROVINCE'].hist(ax=ax2, grid=False, xrot=90, bins=50)
以上就是我们的案例演示部分,我们可以将短期融资券、超短期融资券、中期票据、交易所公司债等信用债的数据,都按照类似的方法提取,并且可以对价格观测的时间进行灵活设定,我们把完全代码列示如下:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from WindPy import w
 
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示正负号
 
 
def trans_code(code, firsttime, lasttime):
    w.start()
    _data = w.wsd(code, "close", firsttime, lasttime, "")
    _data = pd.DataFrame(_data.Data, index=_data.Codes, columns=[x.strftime('%Y-%m-%d') for x in _data.Times])
    return _data
 
 
def get_data(sector, firsttime, lasttime):
    w.start()
    if sector == 'cp':
        _code = w.wset("sectorconstituent", "date={};sectorid=1000004566000000".format(lasttime))
        # 短期融资券-一般短期融资券,500多只
        _code = ','.join(_code.Data[1])
        _data = trans_code(_code, firsttime, lasttime)
        return _data
    elif sector == 'scp':
        _code = w.wset("sectorconstituent", "date={};sectorid=1000004194000000".format(lasttime))
        # 短期融资券-超短期融资券,2200多只
        _code = ','.join(_code.Data[1])
        _data = trans_code(_code, firsttime, lasttime)
        return _data
    elif sector == 'mtn':
        _code = w.wset("sectorconstituent", "date={};sectorid=1000004570000000".format(lasttime))
        # 中期票据-一般中期票据,6200多只
        _code1 = ','.join(_code.Data[1][0:5000])
        _code2 = ','.join(_code.Data[1][5000:])
        _data1 = trans_code(_code1, firsttime, lasttime)
        _data2 = trans_code(_code2, firsttime, lasttime)
        _data = pd.concat([_data1, _data2])
        return _data
    elif sector == 'cb':
        # 一般公司债,3700多只
        _code = w.wset("sectorconstituent", "date={};sectorid=1000009966000000".format(lasttime))
        _code = ','.join(_code.Data[1])
        _data = trans_code(_code, firsttime, lasttime)
        return _data
    elif sector == 'pcb':
        # 公司债-私募债,5100多只
        _code = w.wset("sectorconstituent", "date={};sectorid=1000009967000000".format(lasttime))
        _code1 = ','.join(_code.Data[1][0:5000])
        _code2 = ','.join(_code.Data[1][5000:])
        _data1 = trans_code(_code1, firsttime, lasttime)
        _data2 = trans_code(_code2, firsttime, lasttime)
        _data = pd.concat([_data1, _data2])
        return _data
    else:
        print("You may input wrong arguments.")
 
 
def abnormal(sector, firsttime, lasttime, threshold):
    _data = get_data(sector, firsttime, lasttime)
    _data['diff'] = _data[lasttime] - _data[firsttime]
    _abnormal = _data[_data['diff'] < threshold]
    return _abnormal
 
 
def get_abnormal(sector, firsttime, lasttime, threshold):
    w.start()
    _abnormal = abnormal(sector, firsttime, lasttime, threshold)
    _data = w.wss(','.join(_abnormal.index),
                  "sec_name,issuerupdated,outstandingbalance,maturitydate,term,repo_lastestdate,"
                  "abs_industry,abs_industry1,abs_province,municipalbond",
                  "tradeDate={}".format(lasttime))
    _data = pd.DataFrame(np.array(_data.Data).T, columns=_data.Fields, index=_data.Codes)
    _data = _abnormal.join(_data)
    print(_data)
    return _data
 
 
data_cp = get_abnormal('cp', '2020-11-13', '2020-11-16', -0.5)
data_scp = get_abnormal('scp', '2020-11-13', '2020-11-16', -0.5)
data_mtn = get_abnormal('mtn', '2020-11-13', '2020-11-16', -0.5)
data_cb = get_abnormal('cb', '2020-11-13', '2020-11-16', -0.5)
data_pcb = get_abnormal('pcb', '2020-11-13', '2020-11-16', -0.5)
话题:



0

推荐

经纬

经纬

357篇文章 24天前更新

经济学博士

文章