转债“大时代”系列+简易的转债策略测试框架——以及python实现方法

论坛 期权论坛 期权     
J的行业分享   2019-12-22 17:42   4690   0
可转债市场介绍
关于可转债,从发行到投资你需要知道的
可转债研究框架与历史回顾
探讨可转债投资策略
基于二叉树模型的可转债定价
可转债的Delta对冲套利策略
美国转债市场多大?
美国投资者如何投资可转债?


转债定价专题研究(1):可转债定价方法综述
转债定价专题研究(2):细节决定成败
转债定价,你应该知道的事之一:可转债票面利率规律探析
转债定价,你应该知道的事之二:可转债转股价格和转股溢价率规律探析
转债定价,你应该知道的事之三:债底的作用
转债定价,你应该知道的事之四:条款博弈与价格
转债定价,你应该知道的事之五:退市方式全解析
转债定价,你应该知道的事之六:投资者结构与行为
转债定价,你应该知道的事之七:一级申购的故事
转债定价,你应该知道的事之八:转债的信用风险
转债定价,你应该知道的事之九:转债下修知多少


转债量化投资策略思考(Ⅰ)
转债量化思考(II):转股溢价率与价格的二维分析
转债量化思考(III):存在超涨超跌的机会吗?
转债量化思考(IV):可转债Delta策略


可转债专题之一:历史已退市可转债分析:可转债收益源自何处?
可转债专题之二:可转债定价模型研究
可转债专题之三:银行及券商类转债分析
可转债专题之四:可转债基础知识手册
可转债专题之六:2018年可转债下修回顾
可转债专题之七:2019年可转债下修预测


可转债研究系列一:纵览转债市场,探索“价值投资”
可转债研究系列二:了解转债条款,寻找价值“洼地”
可转债研究系列四:股债轮动:可转债股性和债性的博弈
可转债研究系列五:事件驱动:可转债之转股、赎回与下修
可转债研究系列六:发现价格异动,捕捉转债套利机会


可转债提前赎回条款研究
高价转债风险收益初探
关于转债回售的二三事


转债供给冲击专题研究:千淘万漉虽辛苦,吹尽狂沙始到金
医疗行业转债专题研究:晴空一鹤排云上,便引诗情到碧霄
转债专题研究之新经济转债:黄沙百战穿金甲,不破楼兰终不还


可转债正股投资组合:事件效应下的投资机遇
可转债正股投资组合:探寻可转债正股投资的量化因子


转债“大时代”系列专题之一:转债定价及其希腊字母分析
转债“大时代”系列专题之二:年转债市场回顾
转债“大时代”系列专题之三:基金持仓行为历史大盘点
转债“大时代”系列专题之四:大数据看转债史
转债“大时代”系列专题之五:银行业转债分析手册
转债“大时代”系列专题之六:TMT之手机产业链转债分析手册
转债“大时代”系列专题之七:煤炭、钢铁行业转债分析手册
转债“ 大时代”系列专题之八:汽车整车行业转债分析手册
转债“大时代”系列专题之九:汽车零部件行业转债分析手册
转债“大时代”系列专题之十:新能源汽车产业链转债分析手册
转债“大时代”系列专题之十一:PCB产业链转债分析手册
转债“大时代”系列专题之十二:转债和交换债信用分析初探
转债“大时代”系列专题之十三:电气设备行业转债分析手册
转债“大时代”系列专题之十四:转债择券策略系列:基于量、价、投资指标体系的行业选择
转债“大时代”系列专题之十五:转债择券策略系列:基于财务指标体系的正股选择
转债“大时代”系列专题之十六:转债择券策略系列:基于价格、溢价率和流动性的转债选择
转债“大时代”系列专题之十三:电气设备行业转债分析手册
转债“大时代”系列专题之十八:高价转债的历史表现梳理上篇:绝对价格维度
转债“大时代”系列专题之十九:如何识别下修博弈机会
转债“大时代”系列专题之二十:从货币信用周期看转债在大类资产中的比价关系和轮动规律


[h1]【国信宏观固收】转债“大时代”系列专题汇编[/h1]国信固收研究
国信转债“大时代”系列专题回顾



转债“大时代”系列专题之一:转债定价及其希腊字母分析

转债“大时代”系列专题之三:基金持仓行为历史大盘点
转债“大时代”系列专题之四:大数据看转债史

转债“大时代”系列专题之五:银行业转债分析手册

转债“大时代”系列专题之六:TMT之手机产业链转债分析手册

转债“大时代”系列专题之七:煤炭、钢铁行业转债分析手册

转债“大时代”系列专题之八:汽车整车行业转债分析手册

转债“大时代”系列专题之九:汽车零部件行业转债分析手册

转债“大时代”系列专题之十:新能源汽车产业链转债分析手册

转债“大时代”系列专题之十一:PCB产业链转债分析手册

转债“大时代”系列专题之十二:转债和交换债信用分析初探

转债“大时代”系列专题之十三:电气设备行业转债分析手册
转债“大时代”系列专题之十四:转债择券策略系列:基于量、价、投资指标体系的行业选择

转债“大时代”系列专题之十五:转债择券策略系列:基于财务指标体系的正股选择

转债“大时代”系列专题之十六:转债择券策略系列:基于价格、溢价率和流动性的转债选择

转债“大时代”系列专题之十七:转债择券策略系列:基于行业、公司和转债的综合评分体系

转债专题报告:2017年-2018年转债打分择券策略回测
转债“大时代”系列专题之十八:高价转债的历史表现梳理上篇:绝对价格维度

转债“大时代”系列专题之十九:如何识别下修博弈机会

转债“大时代”系列专题之二十:从货币信用周期看转债在大类资产中的比价关系和轮动规律

转债“大时代”系列专题之二十一:什么是定向可转债?

转债“大时代”系列专题之二十二:关于转债回售的二三事
转债“大时代”系列专题之二十三:国信转债交债风格指数编制

转债“大时代”系列专题之二十四:国信转债交债高流动性指数编制









国信转债投资策略报告回顾   





2019年转债市场策略:底部蛰伏,出击平衡型转债

2018年三季度转债市场回顾
2018年下半年转债市场策略:转债正处于风险收益比极佳的位置
2018年上半年转债市场回顾
2018年春季转债市场策略:守“价值”之正,出“创新”之奇

2018年转债投资策略:“飓风”过岗,“秀木”可存
2017年转债市场回顾


[h1]【中金固收·可转债】简易的转债策略测试框架——以及python实现方法 20190519[/h1]中金固定收益研究


作者杨  冰分析员,SAC执业证书编号: S0080515120002
房  铎联系人,SAC执业证书编号: S0080117080049
姬江帆分析员,SAC执业证书编号:S0080511030008;SFC CE Ref: BDF391




简易的转债策略测试框架[quote]策略好不好,测了才知道 —— 但对于转债来说,可能没那么容易。虽然转债已经基本告别当年迷你市场的窘境,但依然是一个小市场,这是我们在年度回顾报告中的一个判断 —— 而附带的一个影响是,很多配套的东西还不完备,比如策略测试的代码框架。幸好开源的理念之下,这些事情自行处理也并不太复杂。我们在此介绍一个简单的测试框架及其Python实现方法。
首先还是看大的框架,然后再一步一步完成细化实现。大体的流程应该包括:
1、初始化定义:测试的时间段、考虑的转债的范围(比如含不含EB、含不含那些因股改而退市的品种)、调仓周期、以及最终的返回值——策略的净值和必要的记录;
2、进入测试循环:计算净值、并在调仓的时点上进行调仓;
3、返回结果。此时的Python代码如下:


# 引入三个必须引用的库
import datetime as dt
import pandas as pd
import numpy as np

def frameStrategy(obj, start='2015/12/31'):
   '''
   这里的参数还不完全,为了简单先只留最简单的两个
   obj是我们自己设定的一个class,进行日常的转债数据维护和计算,不过此时投资者不必太过在意,因为后面我们将只用其作为数据库的功能obj.DB
   例如 obj.DB['Amt']将返回一个记录转债成交额的pd.DataFrameindexyyyy/mm/dd型的日期,columns是各转债的代码
   '''
   
    # 设定起始日期在库中的位置(我们的数据从2002年开始,这里要返回一个整数,记录start在其中的位置,比如2015/12/31对应的是3391
    # 这个getStartLoc将在后面介绍,后面还有很多这类函数
    intStart = getStartLoc(obj, start)
   
    # dfRet是最终要返回的表,'NAV'这一列就是最重要的了:策略净值(我们这里是100为起点)
    dfRet = pd.DataFrame(index=obj.DB['Amt'].index[intStart:],columns=['NAV','LOG:SEL','LOG:WEIGHT'])
   
    # 这个表记录了持仓,index是转债代码,初始先设定成[Nothing]
    dfAssetBook = pd.DataFrame(index=['Nothing'],columns=['costPrice', 'w'])
   
    # 需要一个变量来记录持仓中的现金(或者借款)
    cash = 100.0

    # 设定转债代码范围
    codes = defineCodes(obj, defineMethod)
   
    # 一个调仓的日期列表,这里设定的是每21个交易日调仓一次
    isAdjustDate = roundOfAdjust(obj, start, 21)

    # 进入循环,enumeratepython里面一个很好用的迭代函数
    for i,date in enumerate(dfRet.index):
      
        # 这一步来记录净值变化
        checkBook(obj, dfRet, dfAssetBook, cash,date)
        # 判定当日是否需要调仓
        if date in isAdjustDate:
            # 如果需要调仓,进入selectCodes函数,根据策略选择个券
            sel = selectCodes(obj, codes, date, selMethod)
            if sel:
                # 这一步得到权重变量
                w = getWeight(obj, sel, date, weightMethod)
            else:
                sel = ['Nothing']
                w = 0.0
               
            dfAssetBook = pd.DataFrame(index=sel, columns=['costPrice', 'w'])
            dfAssetBook['costPrice'] = 100.0
            dfAssetBook['w'] = w
           
    # 无论如何,都用dfRet来记录当日持仓的个券和权重
    # join函数非常实用,用来连接字符串
dfRet['LOG:SEL'][date] =','.join(list(dfAssetBook.index))

    # [func(t) for t in ...] 是非常具备python特色的一个处理方法
    dfRet['LOG:WEIGHT'][date] =','.join([str(t) for t in list(dfAssetBook['w'])])
   
    return dfRet


下面来逐个击破中间的小函数。首先是getStartLoc,实际上pd.DataFrame的index有一个get_loc的方法也能得到这个结果,但早期的版本没考虑过万一要找的变量不在index中怎么办。而后来的版本中,虽然给予了一定容忍度,但也基本没考虑过当index本身是不可比变量时的处理。所以此时我们要进行简单的改造,如下:

def getStartLoc(obj,date):
    # 如果get_loc能解决,就交给它吧
    if date in obj.DB['Amt'].index:
        i= obj.DB['Amt'].index.get_loc(date)

    else:
        # 如果解决不了,要先把index转化成datetime型,而非原本的字符型,这样get_loc就能万用了
       fakeIndex = obj.DB['Amt'].index.map(str2dt)
        i= fakeIndex.get_loc(str2dt(date),method='ffill')

return i

接下来是定义个券大致范围的defineCodes:一般要剔除因股改而退市的那些转债,有时候我们也希望剔除EB。投资者也可以设定其他的规则,这就需要用到一个python特性:函数可以作为参数传入另一个函数。这样的话,投资者可以自行编写一个函数,作为定义范围的方法。实现如下:
def defineCodes(obj,method='default'):

    if method== 'default':
        return obj._excludeSpecial()

    elif method== 'nonEB':
        return obj._excludeSpecial(hasEB=0)

    elif hasatrr(method,’__call__’): # 这一句是判断method是不是一个函数,如果是,则调用这个函数
        return method(obj)

# _excludeSpecial() 是我们的obj中的方法,如下:

def _excludeSpecial(self,hasEB=1):
   columns = set(list(self.DB['Amt'].columns))
    # 这个cb_data.lstSpecial里面存了那些因股改而退市的转债的代码
   columns -= set(cb_data.lstSpecial)
   columns = list(columns)
   
    # 如果不要EB,进入下面的程序
if not hasEB:

        for code in columns:

           if code[:3] == '132' or code[:3] == '120':
               columns.remove(code)
   
    return columns

下面是择券的代码,也是对策略决定意义最大的函数。在调仓日期会调用这个函数。同样,为了给予投资者外部接口,这里也要保留传入函数的可能性。如下:
def selectCodes(obj, codes, date,selMethod=None):

    i = getStartLoc(obj,date)
    n = min([i,5])
    # 这里利用一下pandas.DataFrame的逻辑运算做最基本的条件设定:前5个交易日必须有最少10万的交易
    # 且存量不低于3000

    condition = (obj.DB['Amt'].iloc[i-n:i][codes].fillna(0).min() >100000.0) & \
    (obj.DB['Outstanding'].iloc[codes]> 30000000.0)
   
## 如果selMethod不为空

    If selMethod:
        tempCodes= list(condition[condition].index)
        moreCon= selMethod(obj, codes, date, tempCodes)
   
        condition&= moreCon
   
    # 这个函数最后返回的变量是这个
    retCodes = list(condition[condition].index)
   
    # 如果一个都没有,进入这里,并给出提示
    if not retCodes:
        print 'its a empty selection, when date: ',date
   
    return retCodes

# 下面以低价策略举例,如果我们希望在调仓时买入所有价格低于均价的品种,则可以写下面这个函数,并把_lowprice作为selMethod传入上面的函数:
def _lowPrice(obj, codes, date, tempCodes):

       avgPrice = obj.DB['Close'].loc[date][tempCodes].mean()
return obj.DB['Close'].loc[date, codes]
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:125
帖子:25
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP