引言
卡玛比率(Calmar Ratio) 与夏普比率类似,本来是用来衡量基金业绩表现的指标,描述的是收益和最大回撤之间的关系。计算方式比较简单,为年化收益率与历史最大回撤之间的比率。Calmar比率数值越大,基金的业绩表现越好;反之,基金的业绩表现越差。和夏普比率不同的是,卡玛比率是用最大回撤衡量风险,用年化收益率衡量收益。相比标准差,最大回撤能更准确地衡量基金的风险,尤其控制回撤的能力,因此这个指标又被称为夏普比率的“进阶版”。下面基于沪深京A股数据,使用区间(如20、60、120、200日)收益率和区间最大回撤的比值来衡量Calmar比率,根据结果进行排序,筛选区间强势股。以下选股标的仅供学习参考,不构成任何投资建议,交易有风险,投资需谨慎。
数据获取
下面使用公众号开发的qstock包获取A股市场全部个股的收盘价作为分析样本,qstock基于多线程优点是获取数据非常快,缺点是某些线程可能卡死导致某些个股获取数据失败。大家也可以使用akshare接口替代。
import qstock as qs
import pandas as pd
#获取沪深全市场A股代码
codes=qs.get_code()
#获取沪深全市场A股2020年以来后复权价格数据
#剔除观察值不足250交易日个股
#当网络不太稳定时,可能获取不到全部数据或报错
prices=qs.get_price(code_list=codes,start='20210101',fqt=2)[-250:].dropna(axis=1)
运行结果:
84%|███████████████████████ | 4283/5116 [01:17<00:15, 55.30it/s]
prices.tail()
由于网络或其他原因,使用多线程只获取了4283只个股收盘价数据(占比84%),剔除观测值少于250的样本只剩3626只个股。如果想获取全部A股数据,可以使用akshare接口,但时间比较长,约30分钟左右,具体看get_price2函数。
#如果无法获取全部A股数据,可以使用akshare接口,但时间比较长,约30分钟左右
from tqdm import tqdm
import akshare as ak
def get_price2(code_list, start='20210101', end='20230207', freq='d', fqt=1):
'''code_list输入股票list列表
如code_list=['中国平安','贵州茅台','工业富联']
'''
def run(code):
try:
temp = ak.stock_zh_a_hist(symbol="300114", period="daily", start_date=start, end_date=end, adjust="hfq")
temp[str(code)]=temp['收盘']
data_list.append(temp[str(code)])
pbar.update()
except:
pass
data_list = []
for code in tqdm(code_list):
try:
run(code)
except:
continue
# 转换为dataframe
df = pd.concat(data_list, axis=1)
return df
#使用akshare接口获取全部收盘价数据
#prices2=get_price2(codes)
Calmar比率计算与探索性分析
先以250个交易日为区间考察一下截面价格动量(期间累计收益率)与风险(期间最大回撤)的统计分布特点和相关关系。
#计算kama比率:即期间收益率除以最大回撤
def calmar(s):
ret=(s/s.iloc[0]-1).iloc[-1]
drawdown=(1-s/s.cummax()).max()
kama=ret/drawdown
return kama
data=prices[-250:].dropna(axis=1)
rets=data.apply(lambda s:(s/s[0]-1)[-1])
drawdowns=data.apply(lambda s:(1-s/s.cummax()).max())
calmars=data.apply(lambda s:calmar(s))
calmar_df=pd.concat([rets.to_frame('rets'),drawdowns.to_frame('drawdowns'),calmars.to_frame('calmars')],axis=1)
#注意,这里东方财富数据端口存在一些问题,招商南油后复权价格存在负数,剔除出样本
calmar_df=calmar_df[-(calmar_df['drawdowns']>=1)]
calmar_df.describe()
过去250个交易日,3625个样本里,收益率中位数为-6%,75%分位数为8.8%,最大值253%,最小值为-58.9%,呈现“二八法则”现象,赚钱的个股占少数。最大回撤最大为67.9%,意味着你买入某只股票一直持有250个交易日,最大可能亏损67.9%,最小亏损是3.08%。下面一起来看下哪些个股近一年的最大回撤在10%以内。基本上都是些银行股、还有高速公路等大盘股,这些个股最大回撤低,期间收益率自然也高不起来,毕竟要获得高的收益就得承受一定的高风险。正如佛说,“要想享受鱼肉的鲜美,就得接受鱼刺的纠缠。”
calmar_df[calmar_df['drawdowns']<0.10]
箱线图显示,Calmars比率有较多离群值,主要是因为当最大回撤很小时,计算得到的比值会比较大。
qs.box(calmar_df)
以区间累计收益率为y轴,最大回撤为x轴,可以看出二者并非简单的线性关系,收益率越高,一般最大回撤也越高,但最大回撤越高的时候,可能收益率越低。由此可见,要想获得高收益需要承担一定的高风险,但是承担高风险不必然带来高收益。所以遇到亏损死扛并非是一个好的投资决策。
qs.scatter(x='drawdowns',y='rets',data=calmar_df)
以区间累计收益率为y轴,Calmars为x轴,二者呈现出正向关系(这是因为计算Calmars比率时收益率为分子)。因此使用Calmar比率可以一定程度上筛选出某期间的强势股,当然该指标也有局限性,尤其是当期间最大回撤很小时,得到的比率比较大,但不代表该股价格上很强势。
qs.scatter(x='calmars',y='rets',data=calmar_df)
个股Calmars指标排名
下面以20、60、120、200日区间为例,计算A股的Calmars比率并排序筛选收益率高回撤小的强势个股,这里相当于假设基金买入持有某只个股,然后对该基金的业绩进行评价。
#计算多个期间序列的kama比率并排序
def stock_calmar(data,w_list=[20,60,120,200]):
df=data.copy()
result=pd.DataFrame()
for w in w_list:
temp=df[-w:].dropna(axis=1).apply(lambda s:calmar(s))
result[str(w)+'日']=temp.sort_values(ascending=False)[:50].index
return result
result=stock_calmar(prices)
result
榜上名单大都是近期涨势较好的强势股,根据动量交易策略(强者恒强),再结合个股基本面,可以构建投资组合。
结果可视化
下面使用qstock的可视化模块对部分结果进行可视化展示。
qs.kline(qs.get_data('世运电路')[-20:])
qs.kline(qs.get_data('奥联电子')[-60:])
qs.kline(qs.get_data('惠城环保')[-120:])
qs.kline(qs.get_data('招商南油')[-200:])
# 强势股云图
w_data=qs.ret_top_list(result)
qs.chart_wordcloud(w_data,title=None)
关于Python金融量化
专注于分享Python在金融量化领域的应用。加入知识星球,可以免费获取qstock源代码、30多g的量化投资视频资料、量化金融相关PDF资料、公众号文章Python完整源码、与博主直接交流、答疑解惑等。添加个人微信sky2blue2可获取八五折优惠。