百天计划之38篇,关于“AI智能量化投研平台”搭建。
今天重点要完成可转债“双低”策略的数据补齐。
01 转债一些基础知识
我们先来回顾一下“双低策略”以及它需要什么数据。
双低值=转债价格+转股溢价率*100。
排除:已公告强赎的和一年内到期的。
转债价格(衡量债性):这个就是转债每天的收盘价,这个最直接,我们已经入库了。
转股溢价率(衡量股性)=转债价格÷转股价值-1=转债价格/ (100/转股价*正股价) -1。
看起来有点复杂,但理解一下就比较简单:
100/转股价=可以转成“几张股票”。
张数*正股价,就是转成股票后“市值”是多少。
所以转股溢价率就是 用多少(X)钱(转债价格)买入市场上值多少价值(Y)的东西,比如100块买入120块的东西,溢价率=100/120-1为负的。
溢价的意思就是”买亏“的多少,为负的最好,为零就是不亏不赚,所以溢价肯定越低越高,越低则股性越强。
综上:双低就是寻找当前市场上债性强,股性也强的前N支。
我们还需要补充的数据是转股价,是否公告强赎,以及到期日。
02 转股价
从tushare拉取转股价入mongo:
def get_cb_price_chg(code): # 拉取数据 df = pro.cb_price_chg(**{ "ts_code": code, "limit": "", "offset": "" }, fields=[ "ts_code", "bond_short_name", "publish_date", "change_date", "convert_price_initial", "convertprice_bef", "convertprice_aft" ]) return df def update_all_cb_price_chg(): items = list(get_db()['bond_basic'].find({}, {'ts_code': 1, '_id': 0})) if items and len(items) == 0: logger.error("读可转债列表为空") return for i, item in enumerate(items): code = item['ts_code'] logger.debug("{}-{}-{}".format(i, code, i / len(items))) df = get_cb_price_chg(code) df['_id'] = df['ts_code'] + '_' + df['change_date'] print(df.tail()) write_df('bond_price_chg', df) if __name__ == '__main__': update_all_cb_price_chg()
代码比较简单,这里不做增量更新了,直接全量同步即可。
这就是做转债的好处,因为总共400支,不像主动型基金,你一搞1万多支,同步数据是一个大麻烦。
从mongo入qlib备查。
我们需要把上面这个表变成 转股价时间序列。
我们从mongo读出来的数据如下:
我们需要把转股价变成一列,即如果为Nan,则使用initial初始值替换。
这里主要涉及pandas.Dataframe处理数据的技巧了,做量化投资,pandas的熟练使用会让你事半功倍。
处理之后就是我们的转股价:
def get_price_chg(code): def check_nan(x): if pd.isnull(x['convertprice_aft']): return x['convert_price_initial'] return x['convertprice_aft'] df = get_db()['bond_price_chg'].find({'ts_code': code}, {'change_date': 1, 'convertprice_bef': 1, 'convertprice_aft': 1, 'convert_price_initial': 1, '_id': 0}) df = pd.DataFrame(list(df)) print(df) # 我们使用convertprice_aft 这一列,若此列为空,则使用convert_price_initial df['chg_price'] = df.apply(lambda x: check_nan(x), axis=1) print(df)
return df
序列合并:
df_all = pd.concat([df, df_price_chg], axis=1) df_all.sort_index(inplace=True, ascending=True) df_all.fillna(method='ffill', inplace=True) df_all.sort_index(ascending=True, inplace=True) df_all.dropna(inplace=True)
很重要的一行代码”前向填充ffill“,这样就把这种不规则数据,填充成了时间序列,方便我们后续按天计算。
同理,把正股价查询并合并进来:
数据准备好了,写入csv可以转成qlib数据库。
03 从mongo到csv到qlib数据库
def build_all(path): # path.unlink() codes = get_db()['bond_daily'].distinct(key='ts_code') for i, code in enumerate(codes): logger.debug('下载转债数据{}并存储Csv-{},{}'.format(code, i, i / len(codes))) df = get_data_from_mongo(code) df.rename(columns={'trade_date': 'date', 'vol': 'volume'}, inplace=True) print(df) df.to_csv('{}/{}.csv'.format(path, code), index=False) #break dump = DumpDataAll(csv_path=DATA_DIR_CSV_BOND, qlib_dir=DATA_DIR_QLIB_BOND) dump.dump()
综合步骤,qlib关于可转债的数据库就准备好了。
明天可以就”双低“策略进行回测。
可转债列表页与日频交易数据呈现:fastapi+antV G2
fastapi定时任务,增量构建可转债交易数据入mongo和qlib
飞狐,科技公司CTO,用AI技术做量化投资;以投资视角观历史,解时事;专注个人成长与财富自由。