什么是微笑曲线,顾名思义,这条曲线看起来像微笑的样子,像人开心时微笑的嘴唇。
这只是它表面的意思,更深层次的意思是进行左侧交易,在指数左侧下跌的过程中不断的买入,当进入右侧指数上升的过程中会有不同程度的收益。
先来分析下微笑曲线是怎么给我们带来收益的。
左侧下跌过程中不断买入会摊薄我们的投资成本,右侧上涨过程中达到一定阶段就会产生正收益。上面图中的例子,从1开始买入一份,接下来0.78、0.65、0.52、0.47、0.52、0.65、0.78、1各买入一份,一共买入9份,当买到第8份0.78时,这个时候的成本是0.6713,在这个时候已经获得了16.19%的收益,而买到第9份1时,获得的收益是41.28%。
随着指数不断下跌,买入的份额会更多,成本会越来越低,所以不用等指数回到初始的位置就能获得正收益。
另外,投资过程中是不可能准确找到市场最低点来进行投资的,而依靠微笑曲线能在底部区间不断买入积累低成本的筹码,投资者需要做的非常简单,执行微笑曲线左侧买入即可。
但上面介绍的微笑曲线过程是一个非常理想的投资的过程,在实际的投资过程中需要解决两个问题:
微笑曲线并不微笑上面举的例子中微笑曲线的左侧和右侧是完全对称的,实际情况中有很多类似微笑曲线的时间段,但这些时间段并不是左右对称的,有些时间断是极不规则的。
从上面沪深300的指数走势可以看出,有些位置时左侧陡右侧缓,有些又是反过来的,同时左右的持续长度也不完全对称。
同样中证消费也是一样的。
在实际的投资过程中只要把握整体的走势类似微笑曲线即可,这样的投资过程就能获得收益。
但大家可能发现一个问题,微笑曲线都是从现在看过去,这样是能准确的找到左侧下跌和右侧上涨的过程,但实际的投资过程是并不知道明天未来会发生什么。下面第二点就来讨论这个问题。
微笑曲线的操作今天的投资没有人能预测明天到底是赚钱还是亏损。投资者能做的就是尽量的买得便宜,让自己的成本足够低,在未来获利的概率更大,这个更大的概率是通过估值来进行量化的,当估值进入低估区间就可以开始投资指数,这就是微笑曲线投入的起点,当估值离开低估区间就可以停止投资指数,也就是微小曲线投入的终点。
下面给出沪深300从2010年到2020年底十年来通过微笑曲线投资的收益,投资起始点设定为估值低于50%百分位,结束点设定为估值高于50%百分位。这里为了简单起见,没有考虑卖出(当估值达到高估时卖出,收益会比不卖出大得多)。
上图是通过下面Python的源码运行的结果,大家有兴趣可以参考。
从上面的投资过程中可以看出,基本经历了三次微笑曲线,上半部分中蓝线是沪深300的指数走势,红圈是每次定投1000元的点,可以看到第一次微笑曲线从2010年2月份左右开始,结束于2015年4月左右,第二次微笑曲线开始于2016年1月左右,结束于2017年7月左右,第三次微笑曲线开始于2018年7月左右,结束于2020年7月。三次微笑曲线总共投入资金405000,最终基金总额是665467.32,总的投资收益是64.31%,如果考虑在高估区域卖出的话,投资收益会超过100%。
源码下面是通过沪深300指数结合估值进行历史的一个简单演示,其中smile_curve函数中的thd_valuation = 0.5,这个大家可以修改进行比较,这个值是估值的历史百分位,越高则持续买入的时间越长,越低则持续买入的时间越短。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import math as math
name_index = 'sh.000300'
name_index_g = 'g_hs300'
all_data_index = pd.read_csv('./exportfile/indexDataAll/' + name_index + '.csv')
all_data_index_g = pd.read_csv('./importfile/indexSeries/indexValuation/g/' + name_index_g + '.csv')
calc_range = 2572
calc_gap = 5
data_index_p = all_data_index['close'].values[len(all_data_index['close']) - calc_range:len(all_data_index['close']):calc_gap]
data_index_g = all_data_index_g['pe'].values[len(all_data_index_g['pe']) - calc_range:len(all_data_index_g['pe']):calc_gap]
def smile_curve(val_percentage, val_data_p, buy_cnt):
thd_valuation = 0.5
buy_each_regular_quota = 1000
plot_y = 0
plot_x = 0
if val_percentage < thd_valuation:
buy_each_share = buy_each_regular_quota / val_data_p
buy_cnt = buy_cnt + 1
plot_y = val_data_p
plot_x = i
else:
buy_each_share = 0
return buy_each_share, buy_cnt, [plot_x, plot_y]
buy_each_share_no_regular_quota = np.zeros((len(data_index_p), 1))
buy_total_share_list_no_regular_quota = np.zeros((len(data_index_p), 1))
buy_total_money_list_no_regular_quota = np.zeros((len(data_index_p), 1))
buy_cnt_no_regular_quota = 0
plot_no_regular_quota = np.zeros((len(data_index_p), 2))
idx_start = 1
for i in range(len(data_index_p)):
val_loc = np.where(all_data_index_g['pe'].values[idx_start:len(all_data_index['close']) - calc_range+i*calc_gap:calc_gap] < data_index_g[i])
val_percentage = len(val_loc[0]) / (len(all_data_index_g['pe'].values[idx_start:len(all_data_index['close']) - calc_range+i*calc_gap:calc_gap]))
buy_each_no_regular_quota = 1000
buy_each_share_no_regular_quota[i], buy_cnt_no_regular_quota, plot_no_regular_quota[i] = smile_curve(val_percentage, data_index_p[i],buy_cnt_no_regular_quota)
buy_total_share_list_no_regular_quota[i] = sum(buy_each_share_no_regular_quota) * data_index_p[i]
buy_total_money_list_no_regular_quota[i] = buy_cnt_no_regular_quota * buy_each_no_regular_quota
plt_gap = 6
size_title = 28
size_label = 23
size_line = 3
size_rotation = 0
plt.figure()
plt.rcParams["axes.grid"] = True
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams["grid.linestyle"] = (3, 5)
plt.subplot(211)
plt.plot(data_index_p,linewidth=size_line)
plt_xticks = all_data_index['date'].values[len(all_data_index['close']) - calc_range:len(all_data_index['close']):calc_gap].tolist()
plt.xticks(range(len(plt_xticks),0,-math.floor(len(plt_xticks)/plt_gap)),plt_xticks[len(plt_xticks):0:-math.floor(len(plt_xticks)/plt_gap)],rotation=size_rotation)
plt.tick_params(labelsize=size_label)
income = 100 * (buy_total_share_list_no_regular_quota[-1][0] - buy_total_money_list_no_regular_quota[-1][0]) / buy_total_money_list_no_regular_quota[-1][0]
plt.title('微笑曲线 | 投资收益 = ' + str("{:.2f}".format(income)) + '%',size=size_title)
for i in range(len(plot_no_regular_quota)):
if plot_no_regular_quota[i][0] != 0:
plt.plot(plot_no_regular_quota[i][0], plot_no_regular_quota[i][1],color='tomato',marker='o',ms=10)
plt.subplot(212)
plt.plot(buy_total_share_list_no_regular_quota,color='tomato',linewidth=size_line)
font = {'size': 15, 'color': 'tomato', 'weight': 'black'}
plt.text(len(buy_total_share_list_no_regular_quota)+4, buy_total_share_list_no_regular_quota[-1][0], str("{:.2f}".format(buy_total_share_list_no_regular_quota[-1][0])), fontdict=font)
plt.plot(len(buy_total_share_list_no_regular_quota)-1,buy_total_share_list_no_regular_quota[-1][0], color='tomato', marker='o', ms=15)
plt.plot(buy_total_money_list_no_regular_quota,color='cornflowerblue',linewidth=size_line)
font = {'size': 15, 'color': 'cornflowerblue', 'weight': 'black'}
plt.text(len(buy_total_money_list_no_regular_quota)+4, buy_total_money_list_no_regular_quota[-1][0], str("{:.2f}".format(buy_total_money_list_no_regular_quota[-1][0])), fontdict=font)
plt.plot(len(buy_total_money_list_no_regular_quota)-1,buy_total_money_list_no_regular_quota[-1][0], color='cornflowerblue', marker='o', ms=15)
plt_xticks = all_data_index['date'].values[len(all_data_index['close']) - calc_range:len(all_data_index['close']):calc_gap].tolist()
plt.xticks(range(len(plt_xticks),0,-math.floor(len(plt_xticks)/plt_gap)),plt_xticks[len(plt_xticks):0:-math.floor(len(plt_xticks)/plt_gap)],rotation=size_rotation)
plt.tick_params(labelsize=size_label)
plt.show()
文中用到的两个文件下载链接:https://pan.baidu.com/s/1Wpqm_X7HMmGhVyIlkF6s2A?pwd=ug7a
提取码: ug7a
程序中用到的数据如果有问题,大家可以留言获取也可以添加小将前行的微信xjqx_666进行获取,欢迎大家一起交流沟通
课程参考:基于Python的量化指数基金投资



