本篇文章是我研究涨停系列的最后一篇,也是我认为最有价值的。本文将展示打印每日连板股票的代码,后续持有不同天数的收益情况,以及策略化的回测情况等。
涨停是动量的体现,众所周知动量是一种长期有效的因子,那么市场中动量最高的无疑是连续涨停的股票。当观察到一只股票连板后再买入还来得吗?后续收益怎么样呢?
首先在统计连板股票时应剔除新股与st,因为新股的涨停往往是以少则几个,多则十几个一字板的形式,很难参与,而st股票涨停只有5%非常鸡肋且存在退市风险。
用以下代码在研究环境中进行股票的初步筛选:
```
def filter_new_stock(initial_list,watch_date,n_days):
df=get_all_securities(types=['stock'], date=watch_date)[['start_date']]
df=df.loc[initial_list]
date=datetime.datetime.strptime(all_trade_days[all_trade_days.index(watch_date)-n_days],"%Y-%m-%d").date()
return list(df[df['start_date']< date].index)
def filter_st_stock(initial_list, watch_date):
df = get_extras('is_st', initial_list, start_date=watch_date, end_date=watch_date, df=True)
df =df.T
df.columns = ['is_st']
df = df[df['is_st'] == False]
filter_list = list(df.index)
return filter_list
df = get_all_securities(types=['stock'], date=stat_date)
stock_list = list(df.index)
stock_list = filter_new_stock(stock_list, stat_date, fn)
stock_list = filter_st_stock(stock_list, stat_date)
```
接下来在处理过的股票池中统计连板数,关于函数功能与参数的详细解释可以参考[上一篇](https://www.joinquant.com/view/community/detail/f5819756851809b0052578fb2fc35eeb)文章。找出每日最高连板股票是研究与回测中耗时最多的部分,为了方便,我把研究结果放在了附件中,想打印最近的连板票可以用研究中的代码,想看历史情况可以直接看附件中的统计结果。
找出最高连板票:
```
def limit_count(limit, stock_list, stat_date, watch_days):
if limit == 'high':
df = get_price(stock_list, end_date=stat_date, frequency='daily', fields=['close','high_limit'], count=watch_days, panel=False, fill_paused=False)
df['limit'] = df['high_limit']
elif limit =='low':
df = get_price(stock_list, end_date=stat_date, frequency='daily', fields=['close','low_limit'], count=watch_days, panel=False, fill_paused=False)
df['limit'] = df['low_limit']
df.index = df.code
count_list = []
for stock in stock_list:
df_sub = df.loc[stock]
limit_days = df_sub[df_sub.close==df_sub.limit].close.count()
count_list.append(limit_days)
df = pd.DataFrame(columns=['code','count'])
df['code'] = stock_list
df['count'] = count_list
df = df.dropna()
return df
continue_count_df = []
days_count = wd_count + 1
while len(continue_count_df) == 0:
days_count -= 1
if days_count < = 1:
break
else:
MKT_df = limit_count(limit_condition, stock_list, stat_date, days_count)
continue_count_df = MKT_df[MKT_df['count'] == days_count]
```
在统计出每日连板票后还有一个问题,如果当天符合条件的股票不止一个,应该选哪只?统计表明,市值小的后续收益一般更好,但也不宜过小,市值的标准在不同年份也稍微有些不同。不过为了统计方便,仅保留最小一只。
符合连板条件的股票大于一只时选择最小市值股票:
```
def get_smallest(stock_list, stat_date):
q = query(valuation.code,valuation.circulating_market_cap).filter(valuation.code.in_(stock_list)).order_by(valuation.circulating_market_cap.asc())
df = get_fundamentals(q, date=stat_date)
df.index = df.code
stock = list(df.code)[0]
cap = df.loc[stock]['circulating_market_cap']
return (stock, cap)
```
游资中有一种说法,就是打板需要符合情绪周期,在上升期操作,在下降期空仓。对于情绪周期的量化,我参考了[vinjie2018](https://www.joinquant.com/user/cf50e5163bf24619a05d2d4fa53f6dc0)的做法。在后续统计中证明确实可以提高收益。关于情绪周期的计算公式如下:
情绪周期 = 当前最高连板数 - 过去一段时间最高连板数的最大值
比如最近三天最高连板数为(2,3,4),则计算出的情绪周期数值为4-4=0。如果为(5,3,4),则情绪周期是4-5=-1。何为上升期,就是今天情绪指标创出新高的日子。即只有当情绪周期等于0时操作。
情绪周期示例,可以理解为下图中只有当两条线重合时才操作

计算情绪周期:
```
if days_count > 1:
smallest_stock = get_smallest(list(continue_count_df['code']), stat_date)[0]
smallest_cap = get_smallest(list(continue_count_df['code']), stat_date)[1]
count_df = continue_count_df.copy()
count_df.index = count_df['code']
limit_counts = count_df.loc[smallest_stock]['count']
temp_count_list.append(limit_counts)
if len(temp_count_list)>(emo_cycle-1):
emotion = temp_count_list[-1] - max(temp_count_list[-emo_cycle:])
else:
emotion = 0
```
到这里,我们已经完成了选股与择时的工作,那么要如何评估后续的收益呢?
方法一:T日收盘后选出股票,T+1日以开盘价买入,T+N日以收盘价卖出。计算持有N天的收益。
方法二:买入卖出时间与一相同,但是收益算法不同,收益等于持有时间内最高价与最低价的均值
方法一的统计结果在附件“统计后续收益.xlsx”中以k表示,例如k5就是持有5天的收益。为什么要设计方法二,因为我想了解它最大盈亏空间的情况,周期为5时这部分结果以a5表示。
在统计收益时我发现聚宽的数据有一些与炒股软件中的对应不上,可能因为分红送转导致计算方法不同的原因开头结尾对不上,但是算收益是算中间的差值,所以应该影响不大。
从2008-2021年的统计结果来看,无论基于哪种方法,其平均收益都为正数,且大于沪深300的对照组。
持有天数N越大总收益越高,收益波动越小,但平均到每一天的收益越低。持有天数大于5天之后一般a5会逐渐接近并大于k5,可能因为最大值的中位数在5天以前,最小值的中位数在5天以后。所以一般持有时间应不大于5天。例外情况最典型的是2014年,表现为持有时间越长越好。下图展示的是上述两种方法计算的,持有2至10天的总收益除以天数得到的平均每日收益。

另外,统计的excel文件中还包含连板数和市值的记录,大家可以通过筛选查看几板收益较好。一般来说市值在20-80亿之间收益变化不大,近几年市值小于20亿的收益相对低些,这个现象在几年前并不明显。大于80亿的只有在14,15,17表现还可以,其它时间则表现较差。
有时可能会连续几天信号为同一只股票,那么是要每一次出信号都买呢,还是只在每只股票第一次出现时操作呢?在附件“连板统计1.0”中我统计了连续信号都交易的情况,在2.0中则忽略了后续重复信号。“连板统计2.0.excel"文件的”周期内“分页为只选择emotion=0的收益情况。
统计相关函数:
```
def get_future_price_summary(stock, stat_date, watch_date, sellable_date, last_keep_date):
df = get_price(security=stock, end_date=watch_date, fields=['open'], count=1, panel=False)
start_price = df.iloc[0]['open']
df = get_price(security=stock, end_date=last_keep_date, fields=['close'], count=1, panel=False)
over_price = df.iloc[0]['close']
df = get_price(security=stock, start_date=sellable_date, end_date=last_keep_date, fields=['high','low'], panel=False)
max_price = df['high'].max()
min_price = df['low'].min()
gain = round(((max_price - start_price)/start_price), 2)
lose = round(((min_price - start_price)/start_price), 2)
average = round((0.5 * (gain + lose)), 2)
keep = round((over_price - start_price)/start_price, 2)
return (start_price, min_price, max_price, over_price, lose, gain, average, keep)
```
**总结来说,收益均值的排序从小到大是沪深300对照 < 连板信号不去重出一次买一次 < 连板信号只买首次出现 < 首次出现的信号不择时全部参与 < 只参与首次信号且为情绪周期内。出现信号后参与时间越短资金利用率越高,收益越高**
有了上述理论支持,我们就可以把它写成策略。
1.在T日收盘后计算首次出现的最高连板票,检查当前是否处于可操作的情绪周期内,如果处于,则返回股票代码,否则返回0
2.在T+1开盘时以开盘价买入,这里可能会有涨停开盘买不到的问题,这种情况的影响后续再说,先用强制撮合函数假设可以成交。
3.如果计划持有3天,则分两仓操作,子账户1在T+1日买入,账户2在T+2日如果有信号则买入新一只股票。T+3日如果又有信号,则卖出子账户1中股票买入新股票。T+4日操作子账户2。以此类推。每个账户每次只买入不超过8成的仓位。
由于这个策略回测起来时间特别长,如果积分不多的不建议回测长期,我会把08年至今的回测结果贴在下面。时间长的主要原因在于T日选股,如果有需要研究每日选股的朋友,可以直接看附件中的“每日结果可上传研究.csv”,其中的date为T日的日期,code为当时选出的股票。用本地数据可以快速优化交易逻辑。不过量价策略的本质是抢板凳的游戏,所以希望大家有自己的买点卖点避免冲突,我上面所写仅作为一个抛砖引玉的例子。
最后说一下这个策略的问题,就是上面2中提到的如果涨停开盘怎么办。如果涨停开盘的股票全部视作交易失败,则收益会非常平庸。
实际操作时的情况要复杂的多,仅通过回测很难分析,下面列出几种我认为可能的解决方案:
1.隔夜下单,如果幸运也许可以成交。
2.涨停后打开,由于我的策略只写了在9:30分运行,这种打开的情况不会再去下单。所以实际中只要是符合条件的股票开板了其实是机会。
3.我之前提到了在每日选股时会只保留最小市值的股票,如果恰好这一天有市值比它大一些但没有涨停的符合条件的股票,也可以作为备选。
4.通过对后续走势的评估,不去机械的卖出,比如我策略中选择第三天无脑卖出,但如果买入后继续涨停没有放量,也可以考虑多持有一段时间。亦或者是结合其它技术指标卖出。
5.统计之前没买到的股票开板后收益,这类股票之前没买到,说明比较强势,也许开板后还有机会,可以等开板后买入。
6.如果是有实力的资金,还可以尝试在T日收盘前人造信号,把有潜力,快涨停了的股票买成第二天其他人会根据这个策略买入的股票,从而抢占先机。
这个策略的核心是抓涨停股票短期的动量,但从长期来看,历史上涨停越多的股票,一但动量期结束,后续收益较市场均值来说普遍差很多。而且打板很容易吃面,满仓腰斩后要赚一倍才能回本,同样是先亏50%再赚100%,如果分成两个仓位则平均下来可以赚25%,所以仓位控制很重要。另外,感觉研究涨停形态很容易陷入过度优化,往往一段时间内收益颇高,但对未来操作很难说有什么指导意义。最后,还是建议大家根据自己的风险承受能力决定是否要追连板股票。
**反思:此策略跟踪半年后发现波动巨大,且追涨停的下单成交与否很难控制,感觉价值远不如预期,且回测耗时过长,故删除回测,将速度优化后的源代码放入附件仅供参考。**
@wywy1995 要是随随便便就能写出来,那人人都成高手了;
那是不可能的;
2022-03-27
 请问一创实盘总是这个出错,该任何替换呢,小白求教
2022-03-27
@lwjhtl 我不用一创,你可以看看这篇帖子https://www.joinquant.com/view/community/detail/ad1066ba09de33676c6b6991a1ff599f。或者查看一创上的文档,get_price的参数应该怎么改。
2022-03-27
@wywy1995你不用一创实盘吗?只是做研究?还是在别的平台实盘
2022-03-27
@lwjhtl 我是根据策略手动交易的,很早以前三个股票账户就开满了所以懒得弄一创了
2022-03-27
@lwjhtl 而且善意的提醒你一下,回测中的代码用了强制撮合,即假设委托时已经涨停也可以成交,所以劝你还是用来选股就好了。
2022-03-27
@wywy1995 大佬强制撮合取消后,效果如何啊?
2022-03-28