• 可视化上证50结构图


    缘由

    前些日子,看到sklearn 官网上一个事例,可视化的股市结构 ,由于萌发给咱沪深也整张图,经过一段时间的研究,现分享给大家。

    收集数据

    原文,是数据是来自2003年至2008年 data.nasdaq.com和alphavantage.co等API中获得。
    本文的数据 采用 baostock 2020-至今天(2023-09-30)的交易数据(除权),选择上证50成分股。

    import numpy as np
    import pandas as pd
    import baostock as bs
    
    • 1
    • 2
    • 3
    先获取50支成分股列表
    # 登陆系统
    lg = bs.login()
    # 获取上证50成分股
    rs = bs.query_sz50_stocks()
    # 打印结果集
    sz50_stocks = []
    while (rs.error_code == '0') & rs.next():
        # 获取一条记录,将记录合并在一起
        sz50_stocks.append(rs.get_row_data())
    result = pd.DataFrame(sz50_stocks, columns=rs.fields)
    bs.logout()
    
    df=result.iloc[:,1:3]
    df1=df.set_index('code')
    symbol_dict =df1.to_dict()['code_name']
    symbols, names = np.array(sorted(symbol_dict.i
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    如下图
    在这里插入图片描述

    获取各成分股票K线数据
    def get_kdata(code):
        rs = bs.query_history_k_data_plus(code,
            "date,open,close",
            start_date='2020-01-01', end_date='2023-09-30',
            frequency="d", adjustflag="3")
        data_list = []
        while (rs.error_code == '0') & rs.next():
            data_list.append(rs.get_row_data())
        result = pd.DataFrame(data_list, columns=rs.fields)
        return result
    bs.login()
    quotes = []
    # 
    for symbol in symbols:
        #print(symbol)
        ddf=get_kdata(symbol)
        quotes.append(ddf)
    bs.logout()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    数据处理

    由于这里有些股票数据个数不一致,导致后继执行出错,因此,将不一致的数据进行删除。

    for i,quote in enumerate(quotes):
        if (len(quote)!=910):
            print(i,symbols[i],names[i],len(quote))
    
    • 1
    • 2
    • 3

    23 sh.600905 三峡能源 563
    42 sh.601995 中金公司 711
    49 sh.688599 天合光能 806

    找出来,再删除,然后重新下载数据

    那样的话,并非50支票,而是47支。

    result.drop(labels=[23,42,49],inplace=True)
    df=result.iloc[:,1:3]
    df1=df.set_index('code')
    symbol_dict =df1.to_dict()['code_name']
    symbols, names = np.array(sorted(symbol_dict.items())).T
    bs.login()
    quotes = []
    for symbol in symbols:
        ddf=get_kdata(symbol)
        quotes.append(ddf)
    bs.logout()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    最终获得每日报价的变化值
    close_prices = np.vstack([q["close"].astype("float") for q in quotes])
    open_prices = np.vstack([q["open"].astype("float") for q in quotes])
    
    # 报价的每日变化是信息最多的地方
    variation = close_prices - open_prices
    
    • 1
    • 2
    • 3
    • 4
    • 5

    图形结构处理

    我们使用稀疏逆协方差估计来找出哪些引号与其他引号有条件地相关。具体来说,稀疏逆协方差给了我们一个图,这是一个连接列表。对于每个符号,它所连接的符号都是用来解释其波动的。

    协方差矩阵的逆矩阵,通常称为精度矩阵(precision matrix),它与部分相关矩阵(partial correlation matrix)成正比。 它给出部分独立性关系。换句话说,如果两个特征在其他特征上有条件地独立, 则精度矩阵中的对应系数将为零。这就是为什么估计一个稀疏精度矩阵是有道理的: 通过从数据中学习独立关系,协方差矩阵的估计能更好处理。这被称为协方差选择。

    在小样本的情况,即 n_samples 是数量级 n_features 或更小, 稀疏的逆协方差估计往往比收敛的协方差估计更好。 然而,在相反的情况下,或者对于非常相关的数据,它们可能在数值上不稳定。 此外,与收敛估算不同,稀疏估计器能够恢复非对角线结构 (off-diagonal structure)。

    from sklearn import covariance
    
    alphas = np.logspace(-1.5, 1, num=10)
    edge_model = covariance.GraphicalLassoCV(alphas=alphas)
    
    #标准化时间序列:使用相关性而不是协方差
    #前者对结构恢复更有效
    X = variation.copy().T
    X /= X.std(axis=0)
    edge_model.fit(X)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    聚类分析

    使用affinity_propagation(亲和传播)聚类

    我们使用聚类将行为相似的引号分组在一起。在这里,在scikit-learn中可用的各种集群技术中,我们使用Affinity Propagation,因为它不强制执行相同大小的集群,并且它可以从数据中自动选择集群的数量。
    请注意,这给了我们一个与图不同的指示,因为图反映了变量之间的条件关系,而聚类反映了边际性质:聚集在一起的变量可以被认为在整个股市水平上具有类似的影响。

    Affinity Propagation是一种基于图论的聚类算法,旨在识别数据中的"exemplars"(代表点)和"clusters"(簇)。与K-Means等传统聚类算法不同,Affinity Propagation不需要事先指定聚类数目,也不需要随机初始化簇心,而是通过计算数据点之间的相似性得出最终的聚类结果。

    Affinity Propagation算法的优点是不需要预先指定聚类数目,且能够处理非凸形状的簇。但是该算法的计算复杂度较高,需要大量的存储空间和计算资源,并且对于噪声点和离群点的处理能力较弱。

    from sklearn import cluster
    
    _, labels = cluster.affinity_propagation(edge_model.covariance_, random_state=0)
    n_labels = labels.max()
    
    for i in range(n_labels + 1):
        print(f"Cluster {i + 1}: {', '.join(names[labels == i])}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Cluster 1: 中信证券, 上汽集团, 航发动力, 中信建投, 华泰证券
    Cluster 2: 包钢股份, 北方稀土, 国电南瑞
    Cluster 3: 三一重工, 万华化学, 恒力石化, 紫金矿业, 中远海控
    Cluster 4: 通威股份, 隆基绿能, 长城汽车, 合盛硅业, 华友钴业
    Cluster 5: 复星医药, 恒瑞医药, 片仔癀, 贵州茅台, 海尔智家, 山西汾酒, 伊利股份, 中国中免, 药明康德, 海天味业
    Cluster 6: 中国神华, 陕西煤业, 中国石油
    Cluster 7: 中国石化, 农业银行, 工商银行
    Cluster 8: 招商银行, 兴业银行, 中国平安, 中国太保, 中国人寿
    Cluster 9: 保利发展, 海螺水泥, 长江电力, 中国建筑, 中国电建
    Cluster 10: 闻泰科技, 韦尔股份, 兆易创新

    嵌入二维平面空间

    为了出于可视化目的,我们需要在二维画布上显示不同的符号。为此,我们使用流形学习技术来检索2D嵌入。我们使用密集的本征分解器来实现再现性(arpack是用我们不控制的随机向量启动的)。此外,我们使用大量的邻居来捕捉大规模的结构。

    #为可视化寻找低维嵌入:找到 最佳的二维平面位置(股票)
    
    from sklearn import manifold
    
    node_position_model = manifold.LocallyLinearEmbedding(
        n_components=2, eigen_solver="dense", n_neighbors=6
    )
    embedding = node_position_model.fit_transform(X.T).T
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可视化

    3种模型的输出组合成一个二维平面图,其中节点表示股票,边缘表示:

    • 集群标签用于定义节点的颜色
    • 使用稀疏协方差模型来显示边的强度(边的宽度)
    • 二维嵌入用于定位平面中的节点

    这个示例包含大量与可视化相关的代码,因为可视化在这里对于显示图形至关重要。其中一个挑战是定位标签,使重叠最小化。为此,我们使用基于每个轴上最近邻居方向的启发式方法。

    import matplotlib.pyplot as plt
    from matplotlib.collections import LineCollection
    plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
    plt.rcParams['axes.unicode_minus']=False #用来正常显示负号 #有中文出现的情况,需要u'内容'
    
    plt.figure(1, facecolor="w", figsize=(20, 16))
    plt.clf()
    ax = plt.axes([0.0, 0.0, 1.0, 1.0])
    plt.axis("off")
    
    # Plot the graph of partial correlations
    partial_correlations = edge_model.precision_.copy()
    d = 1 / np.sqrt(np.diag(partial_correlations))
    partial_correlations *= d
    partial_correlations *= d[:, np.newaxis]
    non_zero = np.abs(np.triu(partial_correlations, k=1)) > 0.02
    
    # Plot the nodes using the coordinates of our embedding
    plt.scatter(
        embedding[0], embedding[1], s=100 * d**2, c=labels, cmap=plt.cm.nipy_spectral
    );
    
    # Plot the edges
    start_idx, end_idx = np.where(non_zero)
    # a sequence of (*line0*, *line1*, *line2*), where::
    #            linen = (x0, y0), (x1, y1), ... (xm, ym)
    segments = [
        [embedding[:, start], embedding[:, stop]] for start, stop in zip(start_idx, end_idx)
    ]
    values = np.abs(partial_correlations[non_zero])
    lc = LineCollection(
        segments, zorder=0, cmap=plt.cm.hot_r, norm=plt.Normalize(0, 0.7 * values.max())
    )
    lc.set_array(values)
    lc.set_linewidths(15 * values)
    ax.add_collection(lc)
    
    # Add a label to each node. The challenge here is that we want to
    # position the labels to avoid overlap with other labels
    for index, (name, label, (x, y)) in enumerate(zip(names, labels, embedding.T)):
    
        dx = x - embedding[0]
        dx[index] = 1
        dy = y - embedding[1]
        dy[index] = 1
        this_dx = dx[np.argmin(np.abs(dy))]
        this_dy = dy[np.argmin(np.abs(dx))]
        if this_dx > 0:
            horizontalalignment = "left"
            x = x + 0.002
        else:
            horizontalalignment = "right"
            x = x - 0.002
        if this_dy > 0:
            verticalalignment = "bottom"
            y = y + 0.002
        else:
            verticalalignment = "top"
            y = y - 0.002
        plt.text(
            x,
            y,
            name,
            size=10,
            horizontalalignment=horizontalalignment,
            verticalalignment=verticalalignment,
            bbox=dict(
                facecolor="w",
                edgecolor=plt.cm.nipy_spectral(label / float(n_labels)),
                alpha=0.6,
            ),
        )
    
    plt.xlim(
        embedding[0].min() - 0.15 * embedding[0].ptp(),
        embedding[0].max() + 0.10 * embedding[0].ptp(),
    )
    plt.ylim(
        embedding[1].min() - 0.03 * embedding[1].ptp(),
        embedding[1].max() + 0.03 * embedding[1].ptp(),
    )
    
    plt.show();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    sz50

    小结

    从上图可以看出同类股票,并非分布在同一区域,距离的远近也反映了两两的相关性。
    从一个侧面也反映了各股票之间的关系。如果有可能,将二维平面扩展成三维立体图,可能会更加直观。

    热力图

    基于相关性,本文引入了热力图来进一步可视化。

    import seaborn as sns
    fig = plt.figure(figsize= (15,10))
    ax = fig.add_subplot(111)
    sns.set()
    ax = sns.heatmap(edge_model.covariance_)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    heat
    从上图可以看出,相关性绝大多数在0.2 左右,只有小数达到0.8以上。

  • 相关阅读:
    RabbitMQ之延迟队列
    26.原子类操作类
    RHCE学习 --- 第八次作业(ansible)
    ps -ef|grep test的意思
    嵌入式基础准备 | Linux命令(包括 文件、目录和压缩、系统操作、vi、vim、ctags、cscope)
    用JavaScript撸一个静态链表
    iOS - 多线程-atomic
    C#界面里Form.HelpButton 属性的使用
    好文学作品的鉴赏标准
    Kubernetes弃用Docker?别误解!这份理论实战都具备的手册还得继续学习呀
  • 原文地址:https://blog.csdn.net/cndrip/article/details/133843567