IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    股票分析(三):谁与我生死与共

    CallMeWhy发表于 2016-03-24 12:41:24
    love 0

    背景介绍

    刚开始炒股的时候,心中一直有个疑问:什么是大盘?为什么大家都在看大盘而不是看自己的股票呢?大盘的涨跌和个股的走势有什么关联吗?

    然后看新闻资讯的时候,也常常听到什么拉动所在板块上升之类的话。某个行业的利多利空消息是否对所有股票都适用?哪些个股比较随大流,它涨我也涨?又是哪些股总是带头冲锋,苟利国家生死以,岂因祸福避趋之?

    这篇文章,尝试从数据出发,挖掘股票价格之间的关系,寻找那些生死与共的好股友们。

    概念扫盲

    在继续深入之前,先对一些名词有个简单的了解:

    • 大盘:通常指上证综合指数,以上海证券交易所挂牌上市的全部股票为样本,以发行量为权数,以加权平均法计算,以1990年12月19日为基日,基日指数定为100点的股价指数。
    • 板块:由股票组成的群体,因为有某一共同特征而被归类在一起。板块特征可能是地理上的,例如江苏板块、浦东板块;可能是业绩上的,如绩优板块;可能是上市公司经营行为方面的,如购并板块;可能行业分类方面的,如钢铁板块、科技板块、金融板块、房地产板块等。

    收集数据

    可以通过 tushare 下载历年的数据:

    import tushare as ts
    import pandas as pd

    def download(code, name, index=False):
    filename = 'get_h_2000/'+code+'-'+name+str('.csv')
    if os.path.exists(filename):
    os.remove(filename)
    print '%s已存在,删除源文件' % filename
    df = ts.get_h_data(code, start='2000-01-01', end='2016-01-01', index=index)
    df = df.sort_index(ascending=True).reset_index()
    df.to_csv(filename, mode='a', index=False)

    download('000001','上证指数',index=True)

    有需要的朋友可以在 Gist 下载:000001-上证指数-2000-2015.csv。

    观察数据

    通过 plot 方法可以很方便的绘制上证指数这15年的走势:

    可以用 Pandas 自带的 describe() 方法观察一下目前的数据:

    pd.set_option('display.float_format', lambda x: '%.02f' % x)
    df.describe().transpose()

    数据统计如下:

    数字看着比较抽象,我们可以通过箱线图展示统计信息:

    ax = df.boxplot(column=['close','high','low','open'],return_type='axes')

    箱线图展示结果如下:

    从图中可以很清晰的看到各列的最小值、第一四分位数、中位数、第三四分位数、最大值等等。

    接下来再来看看每天的涨幅如何。这里用『收盘价-开盘价』来表示每天的涨跌幅,并且将涨跌幅除以开盘价,计算并绘制涨跌率:

    df['price_change'] = df['close'] - df['open']
    df['price_change_p'] = df['price_change'] / df['open']
    fig,ax = plt.subplots(figsize=(16,6))
    ax.bar(df.index,df['price_change_p'])
    plt.show()

    绘制结果如下:

    可以看到,08年和15年有比较大的起伏,这分别对应着07年和15年的两次牛市。

    通过 hist 方法可以绘制涨跌率的频率分布直方图:

    fig,ax = plt.subplots(figsize=(16,6))
    ax.hist(df['price_change_p'],bins=100)
    plt.show()

    绘图如下:

    可以看到,大盘的涨跌率基本呈正态分布,涨的部分靠近零轴,跌的部分则略为分散。翻译成大白话大致是:涨的天数多,但是磨磨蹭蹭,幅度不大。跌的天数少,但是来势凶猛,幅度较大。

    接下来看下银行板块的个股股价。通过前面的 download 函数下载好数据之后,通过 read 函数读取数据:

    def read(filename):
    path = 'get_h_2000/%s.csv' % filename
    df = pd.read_csv(path)
    df['date'] = pd.to_datetime(df['date'])
    df = df.set_index('date')
    df['price_change'] = df['close'] - df['open']
    df['price_change_p'] = df['price_change'] / df['open']
    return df

    然后列出所有银行股的数据名,并随机选择三个股票作为对比参照,依次读取数据:

    stocks = np.array([
    '000001-平安银行','002142-宁波银行','600000-浦发银行','600015-华夏银行',
    '600016-民生银行','600036-招商银行','601009-南京银行','601166-兴业银行',
    '601169-北京银行','601288-农业银行','601328-交通银行','601398-工商银行',
    '601818-光大银行','601939-建设银行','601988-中国银行','601998-中信银行'])
    dfs = {}
    for name in stocks:
    dfs[name] = read(name)

    stocks_o = np.array(['601233-桐昆股份','002134-天津普林','600362-江西铜业'])
    dfs_o = {}
    for name in stocks_o:
    dfs_o[name] = read(name)

    由于各股上市时间不一致,我们只取2010年往后的数据做展示::

    temp_stocks = np.concatenate((stocks, stocks_o))
    stock_count = len(temp_stocks)
    f, axes = plt.subplots(stock_count, 1, sharex=True, figsize=(16,4*stock_count))
    useds_axes = []
    for name in temp_stocks:
    df = dfs[name] if dfs.has_key(name) else dfs_o[name]
    df = df[df.index > '2010-01-01']
    ax = axes[len(useds_axes)]
    ax.plot(df.index, df['close'], label=name)
    ax.plot(df_000001.index, df_000001['close']/(df_000001['close'][0]/df['close'][0]), label='上证指数')
    ax.set_title(name)
    ax.legend(loc='upper left')
    useds_axes.append(ax)
    plt.show()

    绘图结果如下:

    从图中来看,大部分银行股的起伏十分相似,而且和大盘的走势相近。不过,民生银行、浦发银行、江西铜业,这三个股票和大盘似乎有些不相关。

    接下来可以从数据的角度具体分析一下它们之间的关联程度。

    寻找相关

    在找到相关的特征之前,我们首先要了解有哪些量化关联度的方法。比如三个比较常见度量方法:

    • 欧式距离:两点间的直线距离,不同特征的量级差异对结果影响较大。
    • 皮尔逊相关系数:用于度量两个变量 X 和 Y 之间的线性相关,值介于-1与1之间,对量级不敏感。
    • 余弦相似度:计算两个向量的夹角,注重向量在方向上的差异,对量级不敏感。

    在这里我选择的是皮尔逊相关系数做实验。

    scipy 中自带了 pearsonr 函数,输入 X 和 Y 返回皮尔逊相关系数和 p-value 。p-value 简单来说,就是在假设原假设正确时,出现现状或更差的情况的概率。

    一个小例子演示一下如何使用:

    np.random.seed(0)
    size = 1000
    x = np.random.normal(0, 1, size)
    print stats.pearsonr(x, x + np.random.normal(0, 1, size))
    print stats.pearsonr(x, x + np.random.normal(0, 100, size))

    运行结果是:

    (0.7029971477605752, 6.8891376644515863e-150)
    (-0.027349877570630021, 0.38761043588940391)

    可以看到,噪音较小的那一串数列的相关性更高,p 值也更低。接下来我们尝试用皮尔逊相关系数来探索股价之间的相关性。

    大盘的影响

    先来看下各个银行股和大盘之间的相关性。为了有所参照,我们随机生成了一个数字序列作为参照,完整代码如下:

    p_result = {}
    start_time = '2010-01-01'
    print '----------随机数列----------'
    np.random.seed(0)
    x1 = df_000001[df_000001.index > start_time]['close'].values
    x0 = np.random.normal(0,100,len(x1))
    r = stats.pearsonr(x1, x0)
    p_result['000000-随机数列'] = r
    print '000000-随机数列:{0}'.format(r)

    print '----------随机个股----------'
    for i in stocks_o:
    df = dfs_o[i]
    df = df[df.index > start_time]
    x = df['close'].values
    x1 = [df_000001.loc[si]['close'] for si in df.index.values]
    r = stats.pearsonr(x1, x)
    p_result[i] = r
    print '{0}:{1}'.format(i ,r)

    print '----------银行板块----------'
    for i in stocks:
    df = dfs[i]
    df = df[df.index > start_time]
    x = df['close'].values
    x1 = [df_000001.loc[si]['close'] for si in df.index.values]
    r = stats.pearsonr(x1, x)
    p_result[i] = r
    print '{0}:{1}'.format(i ,r)

    运算结果:

    ----------随机数列----------
    000000-随机数列:(-0.018379229354344717, 0.48345058165702948)
    ----------随机个股----------
    601233-桐昆股份:(0.82394024147340494, 1.4846471908590122e-276)
    002134-天津普林:(0.87070295523388863, 0.0)
    600362-江西铜业:(0.29074317824804269, 1.1115439379128497e-29)
    ----------银行板块----------
    000001-平安银行:(0.83230166489115542, 0.0)
    002142-宁波银行:(0.95071984964658407, 0.0)
    600000-浦发银行:(0.83177504090683074, 0.0)
    600015-华夏银行:(0.86944980096838431, 0.0)
    600016-民生银行:(0.47921539730721252, 6.7671691831972994e-84)
    600036-招商银行:(0.88739800637271515, 0.0)
    601009-南京银行:(0.89395777699351697, 0.0)
    601166-兴业银行:(0.79369148744309614, 6.16023876965882e-313)
    601169-北京银行:(0.91317333851684301, 0.0)
    601288-农业银行:(0.83442809587466238, 0.0)
    601328-交通银行:(0.92295526522170779, 0.0)
    601398-工商银行:(0.84993313219224631, 0.0)
    601818-光大银行:(0.96153251745347235, 0.0)
    601939-建设银行:(0.84982759873987823, 0.0)
    601988-中国银行:(0.88002650449792652, 0.0)
    601998-中信银行:(0.88759530896504257, 0.0)

    为了更好的展示相关性的结果,可以通过柱状图把结果绘制出来:

    prs = []
    pvs = []
    for name in p_result.keys():
    prs.append(p_result[name][0])
    pvs.append(p_result[name][1])

    fig, ax = plt.subplots(figsize=(16,5))
    ind = np.arange(len(prs))
    width = 0.35
    rects1 = ax.bar(ind, prs, width, color='b')
    rects2 = ax.bar(ind + width, pvs, width, color='r')

    ax.set_ylabel('Value')
    ax.set_title('股票与大盘的相关性系数')
    ax.set_xticks(ind + width)
    ax.set_xticklabels(p_result.keys(), rotation='vertical')

    legend = ax.legend((rects1[0], rects2[0]), ('Pearson\'s', 'P-Value'), frameon=True, shadow=True)
    plt.show()

    绘制结果如下:

    可以看到,随机数列的相关性明显很低,另外相关性比较差的还有民生银行、江西铜业、浦发银行、桐昆股份、平安银行。这个和之前的肉眼观测结果是相同的。

    不过,光看价格的相关性并不是很准确,因为不同股票的价位不同,相关性的计算结果可能也会受到影响。其实我们应该关注的是股价涨跌幅的相关性。调整一下代码:

    temp_stocks = np.concatenate((stocks, stocks_o))
    stock_count = len(temp_stocks)
    f, axes = plt.subplots(stock_count, 1, sharex=True, figsize=(16,4*stock_count))
    useds_axes = []
    for name in temp_stocks:
    df = dfs[name] if dfs.has_key(name) else dfs_o[name]
    start_time = '2015-10-01'
    df = df[df.index > start_time]
    df0 = df_000001[df_000001.index > start_time]
    ax = axes[len(useds_axes)]
    ax.bar(df.index, df['price_change_p'], label=name, alpha=0.5, color='r')
    ax.bar(df0.index, df0['price_change_p'], label='上证指数', alpha=0.5, color='g')
    ax.set_title(name)
    ax.legend(loc='upper left')
    useds_axes.append(ax)
    plt.show()

    为了方便展示,只取了2015年最后三个月的数据,展示如下:

    红色超出绿色的部分为个股超出大盘的涨跌幅,绿色超出的部分为大盘超出个股的涨跌幅。从图表来看,相关性不是很明显。

    用代码计算一下相关性:

    p_result = {}
    start_time = '2010-01-01'
    print '----------随机数列----------'
    np.random.seed(0)
    x1 = df_000001[df_000001.index > start_time]['price_change_p'].values
    x0 = np.random.normal(-1,1,len(x1))
    r = stats.pearsonr(x1, x0)
    p_result['000000-随机数列'] = r
    print '000000-随机数列:{0}'.format(r)

    print '----------随机个股----------'
    for i in stocks_o:
    df = dfs_o[i]
    df = df[df.index > start_time]
    x = df['price_change_p'].values
    x1 = [df_000001.loc[si]['price_change_p'] for si in df.index.values]
    r = stats.pearsonr(x1, x)
    p_result[i] = r
    print '{0}:{1}'.format(i ,r)

    print '----------银行板块----------'
    for i in stocks:
    df = dfs[i]
    df = df[df.index > start_time]
    x = df['price_change_p'].values
    x1 = [df_000001.loc[si]['price_change_p'] for si in df.index.values]
    r = stats.pearsonr(x1, x)
    p_result[i] = r
    print '{0}:{1}'.format(i ,r)

    计算结果如下:

    ----------随机数列----------
    000000-随机数列:(-0.01170938101429754, 0.65528444998797009)
    ----------随机个股----------
    601233-桐昆股份:(0.5926213844273559, 1.3768416303873001e-106)
    002134-天津普林:(0.55037020105007528, 1.9031317037476984e-115)
    600362-江西铜业:(0.76021449109055472, 7.0259968513985565e-274)
    ----------银行板块----------
    000001-平安银行:(0.7051512083759307, 6.6891609215087728e-209)
    002142-宁波银行:(0.72612012412782501, 2.4079706518655618e-237)
    600000-浦发银行:(0.7167360945341158, 3.4557547788647287e-225)
    600015-华夏银行:(0.69668869104482156, 4.1601778321466699e-209)
    600016-民生银行:(0.62750442079223367, 3.9126027918308307e-159)
    600036-招商银行:(0.67017991271658717, 1.208446093582849e-187)
    601009-南京银行:(0.70304459154675281, 5.5004160013777059e-215)
    601166-兴业银行:(0.72086996034908235, 2.2042255646943599e-231)
    601169-北京银行:(0.70007100804010169, 1.8158330878777684e-208)
    601288-农业银行:(0.63124751196123652, 4.2460567821792627e-148)
    601328-交通银行:(0.67157367944285129, 7.08859346030525e-190)
    601398-工商银行:(0.59367541046236361, 3.7205953256769839e-138)
    601818-光大银行:(0.66338500488771768, 6.6439318291648056e-164)
    601939-建设银行:(0.64247755967824627, 6.6203452504774864e-169)
    601988-中国银行:(0.60765522028795782, 2.1780341191010662e-146)
    601998-中信银行:(0.62940968812629339, 1.688815329830977e-159)

    涨跌幅的相关性明显不如价格的相关性。绘制图表如下:

    最高的不到0.8,最低0.5,江西铜业反而成了相关性最高的股了,感觉难以置信。不妨计算一下股票和大盘的涨跌一致的比例:

    start_time = '2010-01-01'
    def count_p(name):
    if name == '000000-随机数列':
    return 0
    df = dfs[name] if dfs.has_key(name) else dfs_o[name]
    df = df[df.index > start_time]
    df.loc[:, 'A'] = pd.Series([df_000001.loc[si]['price_change_p'] for si in df.index.values], index=df.index)
    df.loc[:, 'same_change_p'] = (df['price_change_p'] * df['A']) > 0
    vc = df['same_change_p'].value_counts()
    return vc[True] * 1.0 / (vc[False] + vc[True])

    result = map(count_p, p_result.keys())

    将计算结果绘制在前面的一张图上:

    在过去的六年中,江南铜业有76%的概率涨跌与大盘一致,相关性高也可以理解了。

    板块的影响

    通过前面的探索,基本可以确定板块内的股票也是有一定相关性的,这里就不啰嗦了,直接贴出最后的运算结果:

    小结

    以上就是本次股票相关性分析的全部内容了。回顾一下前面的分析,主要有以下问题:

    • 皮尔逊相关系数只对线性相关敏感,而且结果和数据量有关,用于股票涨跌趋势的相关性分析其实并不合适。
    • 对股票价格的分析,光看价格的涨跌趋势是远远不够,股票的分析难点在于特征值的提取。
    • 考虑使用时间序列模型(ARIMA、GARCH)对股票价格进行分析。

    股海茫茫,涨涨跌跌,若即若离,如梦如幻。一首《刀剑如梦》,献给各位股票,以及它们的爱恨情仇。

    我剑 何去何从
    爱与恨 情难独钟
    我刀 割破长空
    是与非 懂也不懂
    我醉 一片朦胧
    恩和怨 是幻是空
    我醒 一场春梦
    生与死 一切成空

    来也匆匆 去也匆匆
    恨不能相逢
    爱也匆匆 恨也匆匆
    一切都随风

    狂笑一声 长叹一声
    快活一生 悲哀一生
    谁与我生死与共



沪ICP备19023445号-2号
友情链接