请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  数理科学 帖子:3197370 新帖:24

N进N+1打板策略1.1.3版

到此一游发表于:10 月 11 日 14:00回复(1)

本策略已经实现大部分卖点和买点,选股没做成交量和换手限制,开板次新也没限制,导致策略在泉峰汽车吃了20个点大面,不然收益再加10%没问题。
其次是没有考虑题材,如果算出当日最强题材,然后对龙头进行排序按优先级打板,收益将大幅提升。
最后就是打板的精髓,情绪周期问题,策略里只写了几个实现要点,并没有实现。
写策略的时候热血沸腾,搞了几天发现这个工程量太大了,要想完全实现个人的想法,至少还得几个月的工作量。

本版本稍微优化了下,回测时间增加了一个月,将年化收益升到了110 %。

2019-9-28更新
看到这么多人克隆了策略,本人就再分享一些好东西,不负大家的积分。

                         九阳真经之2019年龙头周期图

Img

上图是本人呕心沥血写程序制作的2019年龙头周期图,可以说是复盘利器。

下面说说本人对龙头战法的理解,首先,龙头周期是和指数周期绑定的,指数越强龙头高度越高,今年3月份除了好多9板及以上的龙头,在这种大环境下,最好的策略就是重仓打5-6板;其次,随着龙头高度的下降,说明开始到了退潮期,在指数上来看就是指数开始调整,调整初期还可以打3板,调整越往后失败率越高而且一旦没封住就是20个点的大面,到了中期就只能极小仓位参与了;最后,调整末期市场的最高板会杀到3板,甚至是2板,到了二板好说,重仓干第一个3板。如果是3板,在指数开启下一波涨幅之前是龙头试探高度的时间,这里风险还是有点大,一般很难突破5板。指数突破底部震荡区间的时候就是新周期的开始,突破当天打最高板拿到先手是最好的。

2019-10-05更新
升级龙头周期图,龙头、中军,晋级掉队情况一览无遗

2019-10-11更新
新龙头周期图真的是股林界九阳真经,此图价值10位数,本人仅仅修炼了一周就打通了任督二脉,看清了市场的心跳,此更新献给有缘人。策略本人在通宵达旦开发,不久就会拿到策略擂台去装逼,但是打板策略对于想要鱼的人来说是没用的,一推送就板了。但是如果能够研究出策略的买卖时机那就恭喜你,你是那位有缘人。(改名叫九阳真经,这才是最配的武林绝技)

from jqdata import finance 
from pylab import mpl
from matplotlib.ticker import  MultipleLocator
import pandas as pd
import warnings
import numpy as np
import datetime
import matplotlib.pyplot as plt
import time
import tushare as ts

#过滤警告信息
warnings.filterwarnings("ignore")

#是否是一字板
def isYZB(data, i):
    if data.iloc[i]['open'] == data.iloc[i]['low'] \
    and data.iloc[i]['open'] == data.iloc[i]['close'] \
    and data.iloc[i]['open'] == data.iloc[i]['high_limit'] \
    and data.iloc[i]['close']/data.iloc[i-1]['close'] > 1.09:
        return True
    return False

#是否是涨停
def isUpLimit(data, i):
    if data.iloc[i]['high_limit'] == data.iloc[i]['close']\
    and data.iloc[i]['close']/data.iloc[i-1]['close'] > 1.09 :
        return True
    return False

cntUpLmtTime1 = 0
cntUpLmtTime2 = 0
cntUpLmtDay = ''

#计算涨停数
def countUpLimit(data, day, stock):
    #最后一条数据索引
    lastIndex = data.shape[0]-1
    if not isUpLimit(data, lastIndex):
        return 0
    i = lastIndex
    upLimitCnt = 0
    while i >= 0:
        if isUpLimit(data, i):
            upLimitCnt += 1
            i -= 1
        else:
            break
    #如果涨停前面几天都是一字板,那么不算,从可以买入的那天开始算
    firstUpLmt = lastIndex-upLimitCnt+1
    i = firstUpLmt
    while i < lastIndex:
        if isYZB(data, i):
            upLimitCnt -= 1  #减去一字板个数
            i += 1
        else:
            break
    return upLimitCnt

#计算最大值所在列名        
def calcMaxIndex(row):#对每一行处理的函数
    maxValues = row[row==row['max']].dropna(how='all')
    codes = maxValues.index.tolist()
    codes.remove('max')
    row['code'] = codes
    return row

#格式化tushare日期为聚宽格式
def formatTSDateToJoint(data):
    for i in range(len(data)):
        item = data[i]
        data[i] = item[0:4]+'-'+item[4:6]+'-'+item[6:8]
    return data

def formatDate(data):
    for i in range(len(data)):
        data[i] = data[i][:10]
        
    return data

print('\n-------------开始制作-------------')
startTime = time.clock()
#开始统计日期
startDate = '2018-05-01'
startDate = (datetime.datetime.now()+datetime.timedelta(days=-330)).strftime("%Y-%m-%d")
#结束统计日期
endDate = '2019-02-11'
#endDate = (datetime.datetime.now()+datetime.timedelta(days=-1)).strftime("%Y-%m-%d")
#由于聚宽的接口不能没有提供上交所,深交所的交易日期数据,所以使用tushare的
#机智得使用指数行情来获取交易日期^_^
tradeDates = get_price('399001.XSHE', start_date=startDate, end_date=endDate, \
                                 frequency='daily', fields=['open'],\
                                 skip_paused=True, fq='pre')
tradeDates = tradeDates.index.tolist()
#-----------TODO调试代码-----------
#开始统计,如果是次新,那么就去除前20天的数据,这里只统计2018至今的连板
#取得市值符合条件的股票
stocks = get_fundamentals(query(
          valuation.code, valuation.market_cap, valuation.pe_ratio, income.total_operating_revenue
      ).filter(
          valuation.circulating_market_cap < 150,
          valuation.circulating_market_cap > 5
      ).order_by(
          # 按市值降序排列
          valuation.circulating_market_cap.desc()
      ), date = endDate)
stocks = stocks['code'].tolist()

#连板统计结果
continuousLimit = pd.DataFrame([], index=tradeDates)
print('日期和股票准备就绪,耗时%.2f' % (time.clock()-startTime) )

for i in range(30, len(tradeDates)):
    #-----------统计当天最高的连板天数----------------------
    day = tradeDates[i]
    dayProcessTime = time.clock()
    callServerTime = 0
    countUpLimitTime = 0
    cnt = 0
    if '399001.XSHE' not in continuousLimit.columns:
        continuousLimit['399001.XSHE'] = [0]*continuousLimit.shape[0]
    continuousLimit.loc[day, '399001.XSHE'] = 1
    for stock in stocks:
        #print('正在处理:%s %s' % (day, stock))
        #获取历史行情,如果有停牌,那么这个startDate就不能这么取,默认多取20条,
        #也就是停牌超过10交易日很可能就没法统计了
        startDate = tradeDates[0]
        if tradeDates.index(day) > 30:
            startDate = tradeDates[tradeDates.index(day)-30]
        callServerStart = time.clock()
        historyPrice = get_price(stock, start_date=startDate, end_date=day, \
                                 frequency='daily', fields=['open', 'low', 'close', 'high_limit'],\
                                 skip_paused=True, fq='pre')
        callServerTime = callServerTime+time.clock()-callServerStart
        #如果提前30天取数据还不够15条,那么说明停牌时间过长,不在考虑范围
        if historyPrice.shape[0] < 15:
            continue
        latestRow = historyPrice.tail(1)
        #如果最新一条数据不是当天数据,那么是停牌,不统计
        if day not in latestRow.index:
            continue
        countUpLimitStart = time.clock()
        #计算每天个股的连板数
        cnt = countUpLimit(historyPrice, day, stock)
        if cnt >= 1:
            if stock not in continuousLimit.columns:
                continuousLimit[stock] = [0]*continuousLimit.shape[0]
            continuousLimit.loc[day, stock] = cnt  #这个loc非常耗时,无用数据就别存了
        
    progress = ((i-29)/(len(tradeDates)-30))*100
    remainingTime = (time.clock()-dayProcessTime)*(len(tradeDates)-i)
    remainingTime = remainingTime/60
    dayProcessTime = time.clock()-dayProcessTime
    print('%s日处理耗时:%.2f秒=>get_price耗时:%.2f秒,进度:%d%%,预计剩余时间:%.2f分钟' % \
          (day.strftime("%Y-%m-%d"), dayProcessTime, callServerTime, progress, remainingTime))
#计算每天的最高连板数
continuousLimit = continuousLimit[continuousLimit>=1]    
continuousLimit = continuousLimit.dropna(axis=0, how='all')  
continuousLimit = continuousLimit.dropna(axis=1, how='all') 
continuousLimit = continuousLimit.fillna(0)
continuousLimit['max'] = continuousLimit.max(axis=1)
continuousLimit = continuousLimit.apply(lambda x:calcMaxIndex(x),axis=1)
#由于上面公式添加列名之后没有新加列名字,默认为0,所以要替换掉最后一个列名
cols = continuousLimit.columns.tolist()
cols[len(cols)-1] = 'code'
continuousLimit.columns = cols

#保存到文件供下次直接读取分析
continuousLimit.to_csv('./output/continuousLimit.csv', encoding='utf_8_sig')

print('数据统计完毕!')
workTime = int(time.clock()-startTime)/60
print('总处理耗时:%.2f分钟' % workTime)
-------------开始制作-------------
日期和股票准备就绪,耗时0.07
2019-08-26日处理耗时:8.80秒=>get_price耗时:6.34秒,进度:4%,预计剩余时间:3.67分钟
2019-08-27日处理耗时:8.74秒=>get_price耗时:6.20秒,进度:8%,预计剩余时间:3.50分钟
2019-08-28日处理耗时:8.29秒=>get_price耗时:5.89秒,进度:12%,预计剩余时间:3.18分钟
2019-08-29日处理耗时:8.23秒=>get_price耗时:5.86秒,进度:16%,预计剩余时间:3.02分钟
2019-08-30日处理耗时:8.18秒=>get_price耗时:5.85秒,进度:20%,预计剩余时间:2.86分钟
2019-09-02日处理耗时:8.02秒=>get_price耗时:5.70秒,进度:24%,预计剩余时间:2.67分钟
2019-09-03日处理耗时:8.23秒=>get_price耗时:5.84秒,进度:28%,预计剩余时间:2.60分钟
2019-09-04日处理耗时:8.30秒=>get_price耗时:5.90秒,进度:32%,预计剩余时间:2.49分钟
2019-09-05日处理耗时:8.50秒=>get_price耗时:6.05秒,进度:36%,预计剩余时间:2.41分钟
2019-09-06日处理耗时:8.54秒=>get_price耗时:6.09秒,进度:40%,预计剩余时间:2.28分钟
2019-09-09日处理耗时:8.23秒=>get_price耗时:5.82秒,进度:44%,预计剩余时间:2.06分钟
2019-09-10日处理耗时:8.27秒=>get_price耗时:5.88秒,进度:48%,预计剩余时间:1.93分钟
2019-09-11日处理耗时:8.42秒=>get_price耗时:6.01秒,进度:52%,预计剩余时间:1.82分钟
2019-09-12日处理耗时:8.29秒=>get_price耗时:5.91秒,进度:56%,预计剩余时间:1.66分钟
2019-09-16日处理耗时:8.48秒=>get_price耗时:6.05秒,进度:60%,预计剩余时间:1.55分钟
2019-09-17日处理耗时:8.78秒=>get_price耗时:6.29秒,进度:64%,预计剩余时间:1.46分钟
2019-09-18日处理耗时:8.46秒=>get_price耗时:6.05秒,进度:68%,预计剩余时间:1.27分钟
2019-09-19日处理耗时:8.23秒=>get_price耗时:5.85秒,进度:72%,预计剩余时间:1.10分钟
2019-09-20日处理耗时:8.40秒=>get_price耗时:6.00秒,进度:76%,预计剩余时间:0.98分钟
2019-09-23日处理耗时:8.50秒=>get_price耗时:6.04秒,进度:80%,预计剩余时间:0.85分钟
2019-09-24日处理耗时:8.61秒=>get_price耗时:6.14秒,进度:84%,预计剩余时间:0.72分钟
2019-09-25日处理耗时:8.48秒=>get_price耗时:6.06秒,进度:88%,预计剩余时间:0.57分钟
2019-09-26日处理耗时:8.34秒=>get_price耗时:5.95秒,进度:92%,预计剩余时间:0.42分钟
2019-09-27日处理耗时:8.43秒=>get_price耗时:6.00秒,进度:96%,预计剩余时间:0.28分钟
2019-09-30日处理耗时:8.36秒=>get_price耗时:5.97秒,进度:100%,预计剩余时间:0.14分钟
数据统计完毕!
总处理耗时:3.50分钟

制作周期图¶

最上面为龙头,后面接着是4板以上个股,格式为股票名称+隔日涨幅+连板数;接着是晋级统计,N进N+1=炸板数/成功连板数;最后面是面霸统计
from jqdata import finance 
from pylab import mpl
from matplotlib.ticker import  MultipleLocator
import pandas as pd
import warnings
import numpy as np
import datetime
import matplotlib.pyplot as plt
import time
import tushare as ts
from mpl_toolkits.axisartist.parasite_axes import HostAxes, ParasiteAxes

#计算最大值所在列名        
def calcMaxIndex(row):#对每一行处理的函数
    maxValues = row[row==row['max']].dropna(how='all')
    codes = maxValues.index.tolist()
    codes.remove('max')
    row['code'] = codes
    return row
#continuousLimit = pd.read_csv('./output/continuousLimit2014_2015.csv', index_col=0, header=0)
continuousLimit = pd.read_csv('./output/continuousLimit.csv', index_col=0, header=0)

continuousLimit = continuousLimit[:70]
#由于文件中的code数组无法取值,所以这里需要重新计算一遍
continuousLimit = continuousLimit.apply(lambda x:calcMaxIndex(x),axis=1)
#由于上面公式添加列名之后没有新加列名字,默认为0,所以要替换掉最后一个列名
cols = continuousLimit.columns.tolist()
cols[len(cols)-1] = 'code'
continuousLimit.columns = cols


indexPrice = get_price('000001.XSHG', start_date='2016-01-01', end_date='2020-01-01', \
                                 frequency='daily', fields=['close'],\
                                 skip_paused=True, fq='pre')
tradeDates = indexPrice.index.tolist()
for i in range(0, len(tradeDates)):
    tradeDates[i] = str(tradeDates[i])[0:10]

print('数据准备就绪,开始制图、、、')
'''
'''
#绘制指数图
#Y轴最大最小值
maxContLimit = max(continuousLimit['max'].tolist())+1
minContLimit = min(continuousLimit['max'].tolist())-1
figsizeX = continuousLimit.shape[0]*3
figsizeY = (maxContLimit-minContLimit)*3

mpl.rcParams.update({'font.size': 16})
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题,或者转换负号为字
dates = continuousLimit.index.tolist()
x = np.arange(len(dates))
periodIndexPrices = indexPrice[dates[0]:dates[-1]]
periodIndexPrices = periodIndexPrices['close'].tolist()
periods = continuousLimit['max'].tolist()
maxIndexPrice = max(periodIndexPrices)+30
minIndexPrice = min(periodIndexPrices)-30

fig, axPeriod = plt.subplots() 
fig.set_figwidth(figsizeX)
fig.set_figheight(figsizeY)
axPeriod.plot(x, periods, 'r*-', ms=25)
axPeriod.set_xticks(x)
axPeriod.set_xticklabels(dates)
axPeriod.set_xlabel("日期")
axPeriod.set_ylabel("连板数")
axPeriod.set_ylim(minContLimit, maxContLimit)
for xtick in axPeriod.get_xticklabels():
    xtick.set_rotation(50)
    
axIndex = axPeriod.twinx()
axIndex.plot(x, periodIndexPrices, 'o-')
axIndex.set_ylabel("上证指数")
axIndex.set_ylim(minIndexPrice, maxIndexPrice)



#面王统计
noodleKings = []
noodleKingCnt = 0

for i in range(continuousLimit.shape[0]):
    #最高板BEGIN
    #这里是统计今天的龙头,所以用今天的数据
    rowData = continuousLimit.iloc[i]
    stockName = '>>>>>龙 头<<<<<'
    codes = rowData['code']
    for j in range(len(codes)):
        if '399001' in codes[j]:
            continue
        stockName += '\n'
        stock = codes[j]
        stockName +=  get_security_info(stock).display_name
    #连板数   ----------------------------
    contLimit = rowData['max']
    xTextPos = i  #x轴文字位置
    yTextPos = contLimit  #y轴文字位置
    if yTextPos >= 7:
        yTextPos = yTextPos-1
        xTextPos += 0.05
    elif yTextPos == 1:
        yTextPos = 1.3
    else:
        yTextPos += 0.3
    if i == continuousLimit.shape[0]-1:
        xTextPos = i
    elif i == 0:
        xTextPos = i+0.15
    stockName += '['+str(int(contLimit))+']'
    #最高板END
    
    #中军BEGIN
    rowData = continuousLimit.iloc[i]  #注意是前一天的数据
    rowData.pop('code')
    rowData.pop('max')
    startDate = continuousLimit.index.tolist()[i-1]
    endDate = continuousLimit.index.tolist()[i]
    for stock,boards in rowData.iteritems():
        if 3>rowData[stock]:
            continue
        '''
        price = get_price(stock, start_date=startDate, end_date=endDate, \
                             frequency='daily', fields=['open','close','high','high_limit'],\
                             skip_paused=True, fq='pre')    
        if price.empty or price.shape[0]!=2:
            continue
            '''
        #把4板及以上的也加进去
        if rowData[stock] >= 3: 
            name = get_security_info(stock).display_name
            if '中 军' not in stockName:    
                stockName += '\n>>>>>中 军<<<<<'
            if name not in stockName:
                #计算强度
                stockName += '\n'+name+'['+str(int(rowData[stock]))+']'
    #中军END
    stockName += '\n--------------------'
    
    #炸板率 -------------------------------
    fail2For3 = 0
    success2For3 = 0
    fail3For4 = 0
    success3For4 = 0
    fail4For5 = 0
    success4For5 = 0
    fail5For6 = 0
    success5For6 = 0
    rushBoardStocks = {
        'fail2For3':'',
        'success2For3':'',
        'fail3For4':'',
        'success3For4':'',
        'fail4For5':'',
        'success4For5':'',
        'fail5For6':'',
        'success5For6':''
    }
    #拿昨天的连板数据统它们在今天的强度和晋级情况
    if i>0:
        #昨日连板强度BEGIN
        rowData = continuousLimit.iloc[i-1]  #注意是前一天的数据
        rowData.pop('code')
        rowData.pop('max')
        startDate = continuousLimit.index.tolist()[i-1]
        endDate = continuousLimit.index.tolist()[i]
        for stock,boards in rowData.iteritems():
            if 0==rowData[stock]:
                continue
            price = get_price(stock, start_date=startDate, end_date=endDate, \
                                 frequency='daily', fields=['open','close','high','high_limit'],\
                                 skip_paused=True, fq='pre')    
            if price.empty or price.shape[0]!=2:
                continue
            #把4板及以上的也加进去
            if rowData[stock] >= 3: 
                name = get_security_info(stock).display_name
                if name not in stockName:
                    if '今日掉队' not in stockName:    
                        stockName += '\n>>>>今日掉队<<<<'
                    #计算强度
                    strength = (price.iloc[1]['close']/price.iloc[0]['close']-1)*100
                    stockName += '\n'+name+str(' {:.0f}%').format(strength)
                    stockName += '['+str(int(rowData[stock]))+']'
        #昨日连板强度END
            
            #停牌就懒得处理了
            #因为之前统计的是明日的炸板率,所以有炸板情况,但是改成统计今天的炸板率,因为这个循环里面都是今天涨停的,所以有问题
            #这里得拿昨天的数据
            if price.iloc[1]['high'] == price.iloc[1]['high_limit']:
                if price.iloc[1]['high_limit'] == price.iloc[1]['close']:
                    if 2==rowData[stock]:
                        success2For3 += 1
                        rushBoardStocks['success2For3'] += str(stock)+','
                    elif 3==rowData[stock]:
                        success3For4 += 1
                        rushBoardStocks['success3For4'] += str(stock)+','
                    elif 4==rowData[stock]:
                        success4For5 += 1
                        rushBoardStocks['success4For5'] += str(stock)+','
                    elif 5==rowData[stock]:
                        success5For6 += 1
                        rushBoardStocks['success5For6'] += str(stock)+','
                else:
                    if 2==rowData[stock]:
                        fail2For3 += 1
                        rushBoardStocks['fail2For3'] += str(stock)+','
                    elif 3==rowData[stock]:
                        fail3For4 += 1
                        rushBoardStocks['fail3For4'] += str(stock)+','
                    elif 4==rowData[stock]:
                        fail4For5 += 1
                        rushBoardStocks['fail4For5'] += str(stock)+','
                    elif 5==rowData[stock]:
                        fail5For6 += 1
                        rushBoardStocks['fail5For6'] += str(stock)+','
    #面霸统计,这里需要特别注意的是要拿前天和昨天的连板股来统计,前天的连板股,昨天跌一天或者炸板,今天再跌一天
    #昨天的连板今天炸板跌,或者直接跌2种情况
    #计算都是用这三天的数据来计算
        #昨天连板个股
        rowData = continuousLimit.iloc[i-1]
        rowData.pop('code')
        rowData.pop('max')
        for stock,boards in rowData.iteritems():
            if 0==rowData[stock]:
                continue
            noodleKings.append(stock)  #当日所有连板个股都加入面霸统计
        #前天连板个股
        rowData = continuousLimit.iloc[i-2]
        rowData.pop('code')
        rowData.pop('max')
        for stock,boards in rowData.iteritems():
            if 0==rowData[stock]:
                continue
            noodleKings.append(stock)  #当日所有连板个股都加入面霸统计
        startDate = continuousLimit.index.tolist()[i-2]
        endDate = continuousLimit.index.tolist()[i]
        if len(noodleKings) > 0:
            for item in noodleKings:
                #拿到最新2天的数据,检查最近2天是否有炸板,有炸板就从炸板的涨停价开始算,没有就用第三天的收盘价算
                noodlePrice = get_price(item, start_date=startDate, end_date=endDate, \
                        frequency='daily', fields=['close','high','high_limit'],\
                        skip_paused=True, fq='pre')    
                if noodlePrice.shape[0] == 3:
                    #默认从T-2收盘价开始算
                    downStart = noodlePrice.iloc[0]['close']
                    #如果炸板,那么从涨停价开始算
                    if noodlePrice.iloc[1]['high'] == noodlePrice.iloc[1]['high_limit'] \
                        and noodlePrice.iloc[1]['close'] != noodlePrice.iloc[1]['high_limit']:
                        #T-1天炸板,那么从
                        downStart = noodlePrice.iloc[1]['high']
                    if noodlePrice.iloc[2]['high'] == noodlePrice.iloc[2]['high_limit'] \
                        and noodlePrice.iloc[2]['close'] != noodlePrice.iloc[2]['high_limit']:
                        downStart = noodlePrice.iloc[2]['high']
                    downRate = (1 - noodlePrice.iloc[2]['close']/downStart)*100
                    if downRate >= 15:
                        name = get_security_info(item).display_name
                        noodleKingCnt += 1
    noodleKings = []  #清空,以便统计下一个交易日
    explodeRate = ''
    if fail4For5+success4For5+fail3For4+success3For4+fail2For3+success2For3>0:
        stockName += '\n>>>>梯队晋级<<<<'
    if (fail4For5+success4For5)>0:
        explodeRate = '\n4进5={:.0f}败/{:.0f}成'.format(fail4For5, success4For5)
        stockName += explodeRate
    if (fail3For4+success3For4)>0:
        explodeRate = '\n3进4={:.0f}败/{:.0f}成'.format(fail3For4, success3For4)
        stockName += explodeRate   
    if (fail2For3+success2For3)>0:
        explodeRate = '\n2进3={:.0f}败/{:.0f}成'.format(fail2For3, success2For3)
        stockName += explodeRate
    if noodleKingCnt>0:
        stockName += '\n----面霸{:.0f}个----'.format(noodleKingCnt)
    axPeriod.annotate(stockName, xy=(i, contLimit), \
                 xytext=(xTextPos, yTextPos), xycoords='data',\
                arrowprops=dict(facecolor='#4DFFFF', shrink=0.01))
    noodleKingCnt = 0
plt.savefig("./output/龙头周期.png") # save as png
plt.show()
print('制作完毕!')
数据准备就绪,开始制图、、、
制作完毕!

连板复盘¶

周期启动:新周期启动龙头最强,代表科隆股份、金力永磁,有反包,会有小双头
周期递减:如果第二个龙头不能超越第一个,那么开始递减效应,每个龙头的顶部溢价越来越小
周期尾声:龙头隔日跌停,毫无溢价,遍地-15%以上大面股,然后杀到最高板为3-4板;尾部就是龙头溢价降低,同时一字板进入白热化导致大资金不愿意承接,进而引发梯队的崩盘。崩盘后顶一字的少了,大资金开始打造新周期。
import pandas as pd

continuousLimit = pd.read_csv('./output/continuousLimit.csv', index_col=0, header=0)
'''
dateIndex=continuousLimit.index.tolist().index('2019-05-06')
print(continuousLimit[dateIndex-1:dateIndex+4])
continuousLimit = continuousLimit[-60:]
'''

totalFail2For3 = 0
totalSuccess2For3 = 0
totalFail3For4 = 0
totalSuccess3For4 = 0
totalFail4For5 = 0
totalSuccess4For5 = 0
totalFail5For6 = 0
totalSuccess5For6 = 0
for i in range(1, (continuousLimit.shape[0])):
    rowData = continuousLimit.iloc[i-1]
    startDate = continuousLimit.index.tolist()[i-1]
    endDate = continuousLimit.index.tolist()[i]
    stockName = ''
    rowData.pop('code')
    rowData.pop('max')
    fail2For3 = 0
    success2For3 = 0
    fail3For4 = 0
    success3For4 = 0
    fail4For5 = 0
    success4For5 = 0
    fail5For6 = 0
    success5For6 = 0
    rushBoardStocks = {
        'fail2For3':'',
        'success2For3':'',
        'fail3For4':'',
        'success3For4':'',
        'fail4For5':'',
        'success4For5':'',
        'fail5For6':'',
        'success5For6':''
    }
    for stock,boards in rowData.iteritems():
        if 0==rowData[stock]:
            continue
        price = get_price(stock, start_date=endDate, end_date=endDate, \
                             frequency='daily', fields=['close','high','high_limit'],\
                             skip_paused=True, fq='pre')    
        if price.empty:
            continue
        #停牌就懒得处理了
        if price.iloc[0]['high'] == price.iloc[0]['high_limit']:
            if price.iloc[0]['high_limit'] == price.iloc[0]['close']:
                if 2==rowData[stock]:
                    success2For3 += 1
                    totalSuccess2For3 += 1
                    rushBoardStocks['success2For3'] += str(stock)+','
                elif 3==rowData[stock]:
                    success3For4 += 1
                    totalSuccess3For4 += 1
                    rushBoardStocks['success3For4'] += str(stock)+','
                elif 4==rowData[stock]:
                    success4For5 += 1
                    totalSuccess4For5 += 1
                    rushBoardStocks['success4For5'] += str(stock)+','
                elif 5==rowData[stock]:
                    success5For6 += 1
                    totalSuccess5For6 += 1
                    rushBoardStocks['success5For6'] += str(stock)+','
            else:
                if 2==rowData[stock]:
                    fail2For3 += 1
                    totalFail2For3 += 1
                    rushBoardStocks['fail2For3'] += str(stock)+','
                elif 3==rowData[stock]:
                    fail3For4 += 1
                    totalFail3For4 += 1
                    rushBoardStocks['fail3For4'] += str(stock)+','
                elif 4==rowData[stock]:
                    fail4For5 += 1
                    totalFail4For5 += 1
                    rushBoardStocks['fail4For5'] += str(stock)+','
                elif 5==rowData[stock]:
                    fail5For6 += 1
                    totalFail5For6 += 1
                    rushBoardStocks['fail5For6'] += str(stock)+','
    print('------------------------------------')     
    print(endDate)
    if (fail2For3+success2For3)>0:
        print('----2进3炸板率为%.0f%% \n 失败:%s \n 成功:%s' \
              %(fail2For3*100/(fail2For3+success2For3), \
                rushBoardStocks['fail2For3'], rushBoardStocks['success2For3']))  
    if (fail3For4+success3For4)>0:
        print('----3进4炸板率为%.0f%%\n 失败:%s \n 成功:%s' \
              %(fail3For4*100/(fail3For4+success3For4), \
                rushBoardStocks['fail3For4'], rushBoardStocks['success3For4']))  
    if (fail4For5+success4For5)>0:
        print('----4进5炸板率为%.0f%%\n 失败:%s \n 成功:%s' \
              %(fail4For5*100/(fail4For5+success4For5), \
                rushBoardStocks['fail4For5'], rushBoardStocks['success4For5']))  
    if (fail5For6+success5For6)>0:
        print('----5进6炸板率为%.0f%%\n 失败:%s \n 成功:%s' \
              %(fail5For6*100/(fail5For6+success5For6), \
                rushBoardStocks['fail5For6'], rushBoardStocks['success5For6']))   
print('----------------------\n整体炸板率统计:')        
if (totalFail2For3+totalSuccess2For3)>0:
    print('----2进3炸板率为%.0f%%'  %(totalFail2For3*100/(totalFail2For3+totalSuccess2For3)))  
if (totalFail3For4+totalSuccess3For4)>0:
    print('----3进4炸板率为%.0f%%' %(totalFail3For4*100/(totalFail3For4+totalSuccess3For4)))  
if (totalFail4For5+totalSuccess4For5)>0:
    print('----4进5炸板率为%.0f%%' %(totalFail4For5*100/(totalFail4For5+totalSuccess4For5)))  
if (totalFail5For6+totalSuccess5For6)>0:
    print('----5进6炸板率为%.0f%%' %(totalFail5For6*100/(totalFail5For6+totalSuccess5For6)))   
------------------------------------
2019-08-13
----2进3炸板率为0% 
 失败: 
 成功:603920.XSHG,
------------------------------------
2019-08-14
----2进3炸板率为50% 
 失败:300566.XSHE, 
 成功:002751.XSHE,
------------------------------------
2019-08-15
----3进4炸板率为0%
 失败: 
 成功:002751.XSHE,
------------------------------------
2019-08-16
----2进3炸板率为0% 
 失败: 
 成功:600152.XSHG,
----4进5炸板率为100%
 失败:002751.XSHE, 
 成功:
------------------------------------
2019-08-19
----2进3炸板率为0% 
 失败: 
 成功:002194.XSHE,002201.XSHE,002881.XSHE,
----3进4炸板率为0%
 失败: 
 成功:600152.XSHG,
------------------------------------
2019-08-20
----2进3炸板率为50% 
 失败:002402.XSHE,603999.XSHG, 
 成功:300731.XSHE,002886.XSHE,
----3进4炸板率为0%
 失败: 
 成功:002201.XSHE,
----4进5炸板率为0%
 失败: 
 成功:600152.XSHG,
------------------------------------
2019-08-21
----2进3炸板率为17% 
 失败:600812.XSHG, 
 成功:002197.XSHE,002256.XSHE,000038.XSHE,300154.XSHE,000017.XSHE,
----3进4炸板率为0%
 失败: 
 成功:002886.XSHE,
----4进5炸板率为0%
 失败: 
 成功:002201.XSHE,
----5进6炸板率为100%
 失败:600152.XSHG, 
 成功:
------------------------------------
2019-08-22
----3进4炸板率为33%
 失败:000017.XSHE, 
 成功:002256.XSHE,000038.XSHE,
----5进6炸板率为0%
 失败: 
 成功:002201.XSHE,
------------------------------------
2019-08-23
----2进3炸板率为33% 
 失败:300278.XSHE, 
 成功:000056.XSHE,600069.XSHG,
----4进5炸板率为0%
 失败: 
 成功:000038.XSHE,
------------------------------------
2019-08-26
----2进3炸板率为0% 
 失败: 
 成功:600812.XSHG,002750.XSHE,300350.XSHE,002213.XSHE,
----3进4炸板率为0%
 失败: 
 成功:000056.XSHE,
----5进6炸板率为100%
 失败:000038.XSHE, 
 成功:
------------------------------------
2019-08-27
----2进3炸板率为0% 
 失败: 
 成功:002505.XSHE,002219.XSHE,300194.XSHE,000007.XSHE,603701.XSHG,
----3进4炸板率为0%
 失败: 
 成功:600812.XSHG,002750.XSHE,002213.XSHE,
------------------------------------
2019-08-28
----2进3炸板率为0% 
 失败: 
 成功:601777.XSHG,000058.XSHE,600081.XSHG,300733.XSHE,
----3进4炸板率为100%
 失败:002505.XSHE, 
 成功:
----4进5炸板率为100%
 失败:600812.XSHG, 
 成功:
------------------------------------
2019-08-29
----2进3炸板率为20% 
 失败:300775.XSHE, 
 成功:000038.XSHE,600242.XSHG,000595.XSHE,300280.XSHE,
----3进4炸板率为33%
 失败:601777.XSHG, 
 成功:000058.XSHE,300733.XSHE,
------------------------------------
2019-08-30
----2进3炸板率为100% 
 失败:300178.XSHE,600146.XSHG,600589.XSHG, 
 成功:
----3进4炸板率为67%
 失败:600242.XSHG,300280.XSHE, 
 成功:000595.XSHE,
------------------------------------
2019-09-02
----2进3炸板率为33% 
 失败:002536.XSHE, 
 成功:002417.XSHE,300621.XSHE,
----4进5炸板率为0%
 失败: 
 成功:000595.XSHE,
------------------------------------
2019-09-03
----2进3炸板率为40% 
 失败:300581.XSHE,002806.XSHE, 
 成功:601698.XSHG,300672.XSHE,300591.XSHE,
----3进4炸板率为50%
 失败:300621.XSHE, 
 成功:002417.XSHE,
----5进6炸板率为0%
 失败: 
 成功:000595.XSHE,
------------------------------------
2019-09-04
----2进3炸板率为40% 
 失败:000673.XSHE,002098.XSHE, 
 成功:603613.XSHG,603936.XSHG,000032.XSHE,
----3进4炸板率为100%
 失败:300672.XSHE, 
 成功:
----4进5炸板率为0%
 失败: 
 成功:002417.XSHE,
------------------------------------
2019-09-05
----2进3炸板率为0% 
 失败: 
 成功:002941.XSHE,
----3进4炸板率为100%
 失败:603613.XSHG,603936.XSHG, 
 成功:
----5进6炸板率为0%
 失败: 
 成功:002417.XSHE,
------------------------------------
2019-09-06
----2进3炸板率为33% 
 失败:002201.XSHE, 
 成功:002095.XSHE,002134.XSHE,
------------------------------------
2019-09-09
----2进3炸板率为0% 
 失败: 
 成功:600850.XSHG,603011.XSHG,000890.XSHE,300061.XSHE,
----3进4炸板率为50%
 失败:002095.XSHE, 
 成功:002134.XSHE,
------------------------------------
2019-09-10
----2进3炸板率为60% 
 失败:002486.XSHE,300384.XSHE,300492.XSHE, 
 成功:002017.XSHE,603322.XSHG,
----3进4炸板率为50%
 失败:600850.XSHG,000890.XSHE, 
 成功:603011.XSHG,300061.XSHE,
----4进5炸板率为0%
 失败: 
 成功:002134.XSHE,
------------------------------------
2019-09-11
----2进3炸板率为0% 
 失败: 
 成功:300379.XSHE,600052.XSHG,300270.XSHE,
----4进5炸板率为0%
 失败: 
 成功:300061.XSHE,
----5进6炸板率为100%
 失败:002134.XSHE, 
 成功:
------------------------------------
2019-09-12
----2进3炸板率为50% 
 失败:002750.XSHE, 
 成功:002622.XSHE,
----3进4炸板率为0%
 失败: 
 成功:300270.XSHE,
------------------------------------
2019-09-16
----2进3炸板率为0% 
 失败: 
 成功:300480.XSHE,
----3进4炸板率为100%
 失败:002622.XSHE, 
 成功:
------------------------------------
2019-09-17
----2进3炸板率为67% 
 失败:002476.XSHE,300650.XSHE, 
 成功:002819.XSHE,
------------------------------------
2019-09-18
----2进3炸板率为0% 
 失败: 
 成功:300052.XSHE,603121.XSHG,
----3进4炸板率为0%
 失败: 
 成功:002819.XSHE,
------------------------------------
2019-09-19
----2进3炸板率为50% 
 失败:600896.XSHG, 
 成功:002917.XSHE,
----3进4炸板率为0%
 失败: 
 成功:603121.XSHG,
----4进5炸板率为0%
 失败: 
 成功:002819.XSHE,
------------------------------------
2019-09-20
----2进3炸板率为25% 
 失败:300507.XSHE, 
 成功:002786.XSHE,002552.XSHE,300460.XSHE,
----3进4炸板率为0%
 失败: 
 成功:002917.XSHE,
----5进6炸板率为0%
 失败: 
 成功:002819.XSHE,
------------------------------------
2019-09-23
----2进3炸板率为50% 
 失败:300407.XSHE, 
 成功:300587.XSHE,
----3进4炸板率为33%
 失败:300460.XSHE, 
 成功:002786.XSHE,002552.XSHE,
----4进5炸板率为0%
 失败: 
 成功:002917.XSHE,
------------------------------------
2019-09-24
----2进3炸板率为33% 
 失败:002660.XSHE, 
 成功:603023.XSHG,300375.XSHE,
----4进5炸板率为0%
 失败: 
 成功:002786.XSHE,002552.XSHE,
------------------------------------
2019-09-25
----2进3炸板率为0% 
 失败: 
 成功:002316.XSHE,300293.XSHE,603602.XSHG,
----5进6炸板率为0%
 失败: 
 成功:002552.XSHE,
------------------------------------
2019-09-26
----2进3炸板率为0% 
 失败: 
 成功:600122.XSHG,
----3进4炸板率为100%
 失败:002316.XSHE, 
 成功:
------------------------------------
2019-09-27
----2进3炸板率为50% 
 失败:002943.XSHE, 
 成功:603956.XSHG,
----------------------
整体炸板率统计:
----2进3炸板率为27%
----3进4炸板率为39%
----4进5炸板率为15%
----5进6炸板率为38%
import pandas as pd

continuousLimit = pd.read_csv('./output/continuousLimit.csv', index_col=0, header=0)
continuousLimit = continuousLimit[-60:]


stocks = ''
for index, rowData in continuousLimit.iterrows():
    rowData.pop('max')
    rowData.pop('code')
    rowData = rowData[rowData==3]
    size = rowData.size
    cnt += size
    if size>0:
        stocks += str(rowData.index.tolist())
print(stocks)
['600794.XSHG', '600081.XSHG']['600710.XSHG']['002783.XSHE', '002761.XSHE']['002547.XSHE', '600218.XSHG', '600758.XSHG', '300194.XSHE']['600759.XSHG', '600775.XSHG', '600459.XSHG', '300292.XSHE', '300299.XSHE', '300573.XSHE', '300698.XSHE']['002215.XSHE']['600175.XSHG', '300492.XSHE']['600213.XSHG']['002600.XSHE', '300152.XSHE', '002054.XSHE', '000702.XSHE']['002467.XSHE', '000815.XSHE']['300405.XSHE']['600368.XSHG']['002164.XSHE']['600127.XSHG']['600300.XSHG', '000713.XSHE', '603777.XSHG']['300119.XSHE', '002077.XSHE', '300488.XSHE']['002119.XSHE', '000589.XSHE']['300123.XSHE', '300480.XSHE', '603335.XSHG', '300576.XSHE']['300292.XSHE']['000713.XSHE', '002404.XSHE', '300542.XSHE']['300342.XSHE']['002164.XSHE', '600313.XSHG', '300029.XSHE']['000795.XSHE', '600734.XSHG', '300354.XSHE']['002119.XSHE', '300184.XSHE', '300066.XSHE']['002388.XSHE']['600476.XSHG', '300313.XSHE']['300210.XSHE']['300480.XSHE', '300748.XSHE', '600225.XSHG', '300671.XSHE']['300169.XSHE', '603906.XSHG', '002905.XSHE', '300626.XSHE']['002119.XSHE', '300184.XSHE', '600206.XSHG', '603668.XSHG', '002725.XSHE', '300330.XSHE', '603045.XSHG', '603078.XSHG']['600354.XSHG', '600463.XSHG', '002953.XSHE', '603739.XSHG']['600371.XSHG', '601975.XSHG', '002902.XSHE']['000586.XSHE', '603266.XSHG']['603977.XSHG', '603042.XSHG', '300615.XSHE']['300656.XSHE']['600371.XSHG', '300127.XSHE', '603701.XSHG', '600359.XSHG', '002885.XSHE']['002057.XSHE', '603169.XSHG', '300713.XSHE']['002378.XSHE', '300090.XSHE', '002842.XSHE', '300555.XSHE']['000812.XSHE', '002949.XSHE']['300189.XSHE', '603258.XSHG']['000912.XSHE', '600844.XSHG', '300605.XSHE']['002107.XSHE']['601038.XSHG', '600609.XSHG', '300777.XSHE', '002923.XSHE']['300663.XSHE', '002199.XSHE', '002645.XSHE']['002057.XSHE', '300023.XSHE', '300090.XSHE', '300560.XSHE', '601200.XSHG', '603686.XSHG', '002945.XSHE', '601330.XSHG', '300301.XSHE']['300362.XSHE', '600501.XSHG', '603106.XSHG', '300022.XSHE']['300371.XSHE', '300032.XSHE']['300536.XSHE']['603738.XSHG', '300210.XSHE', '300414.XSHE', '300267.XSHE']
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
from pylab import mpl


#plt.rcdefaults()
mpl.rcParams['font.sans-serif'] = ['SimHei'] #解决绘图中文显示问题
mpl.rcParams['axes.unicode_minus'] = False   # 解决保存图像是负号'-'显示为方块的问题

info_list = [(u"小给", 88, 23), (u"小人", 78, 10), (u"小民", 90, 5), (u"小一", 66, 9), (u"小个", 80, 22), (u"小胶", 48, 5), (u"小带", 77