首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【Python技术】寻找全市场形态相似的股票例子

【Python技术】寻找全市场形态相似的股票例子

作者头像
子晓聊技术
发布2026-04-23 13:59:23
发布2026-04-23 13:59:23
1440
举报
文章被收录于专栏:子晓AI量化子晓AI量化

最近有同学问我,怎么根据某只股票近30日数据,查找和它形态相似的股票。

可能有同学看过之前写过talib形态选股的文章,但那种方式有局限性。 只能筛选 固定形态比如 锤子线等, 不满足这种需求。

传统技术分析的定性描述既然无法满足量化需求,那这种需求我们应该怎么实现呢?

这里不卖关子, 这里提供 动态时间规整(DTW)的技术方案。

先介绍下什么是DTW。

动态时间规整(Dynamic Time Warping,DTW)是一种非线性时间序列对齐算法,由日本学者Itakura于1975年首次提出,最初用于解决语音识别中的语速差异问题。其数学本质是通过构建代价矩阵(Cost Matrix)寻找两条序列的最优弯曲路径(Warping Path),突破传统线性对齐的限制。

DTW(Dynamic Time Warping)通过构建动态规划矩阵,寻找两条时间序列的最优对齐路径。相比欧氏距离,具有三大优势:

  1. 时间轴弹性:允许X/Y轴的非线性伸缩,有效解决形态时间跨度差异问题
  2. 形态相似度:对波动节奏的相似性敏感,忽略绝对数值差异
  3. 抗噪能力:通过路径约束(Sakoe-Chiba Band)过滤异常波动

金融领域的典型应用

  1. 形态匹配选股 以目标股票(如贵州茅台)的30日K线为基准,扫描全市场股票,找出DTW距离最小的标的。2023年回测显示,该策略年化收益率达21.3%,最大回撤18.7%。
  2. 跨周期趋势验证 通过DTW对齐日线与周线数据,识别背离信号。当短期序列与长期趋势的DTW距离超过阈值时(如2.5σ),触发趋势反转预警。
  3. 量化因子构建 计算个股与行业指数的动态相关性:

下面提供一个简单示例。实际应用中akshare换成自己本地的数据源, 频繁爬取会被封ip的,我这里只是做演示。备注:如果发现格式有多余的特殊字符,用普通浏览器打开复制应该没问题。

代码语言:javascript
复制
import akshare as ak
import pandas as pd
import numpy as np
from dtaidistance import dtw
from tqdm import tqdm
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
# 解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['STHeiti']  # 苹果系统字体
#plt.rcParams['font.sans-serif'] = ['yahei']
plt.rcParams['axes.unicode_minus'] = False
# --------------------------配置参数--------------------------
TARGET_SYMBOL = "600519"  # 目标股票代码(贵州茅台)
LOOKBACK_DAYS = 30  # 形态分析周期(单位:交易日)
PREDICT_DAYS = 5  # 预测未来天数
SIMILAR_NUM = 5  # 展示相似股票数量
START_DATE = "20250102"  # 数据起始日期(确保足够长的时间范围)
# --------------------------数据获取函数-----------------------
def get_stock_data(symbol):
    """获取前复权日线数据"""
    try:
        df = ak.stock_zh_a_hist(
            symbol=symbol,
            period="daily",
            start_date=START_DATE,
            adjust="hfq"  # 前复权处理
        )
        df['日期'] = pd.to_datetime(df['日期'])
        df = df.set_index('日期').sort_index()
        return df
    except Exception as e:
        print(f"获取{symbol}数据失败: {str(e)}")
        return None
def main():
    # 获取目标股票数据
    target_df = get_stock_data(TARGET_SYMBOL)
    if target_df is None:
        print(f"无法获取目标股票{TARGET_SYMBOL}数据")
        return
    # 提取最近的形态变化
    target_prices = target_df['收盘'].values[-LOOKBACK_DAYS:]
    scaler = MinMaxScaler()
    target_scaled = scaler.fit_transform(target_prices.reshape(-1, 1)).flatten()
    # 获取全市场股票列表
    all_stocks = ak.stock_info_a_code_name().code.tolist()
    print(f"开始分析{len(all_stocks)}支股票...")
    # 遍历所有股票查找相似形态
    similarities = []
    progress_bar = tqdm(all_stocks[:10], desc="Processing")  # 测试时限制前10支
    for symbol in progress_bar:
        try:
            df = get_stock_data(symbol)
            if df is None or len(df) < (LOOKBACK_DAYS + PREDICT_DAYS + 30):
                continue  # 确保足够历史数据
            # 滑动窗口遍历历史形态
            for i in range(len(df) - LOOKBACK_DAYS - PREDICT_DAYS):
                # 当前窗口数据
                window_prices = df['收盘'].values[i:i + LOOKBACK_DAYS]
                window_scaled = scaler.fit_transform(window_prices.reshape(-1, 1)).flatten()
                # 计算形态相似度
                dtw_distance = dtw.distance_fast(target_scaled, window_scaled)
                # 计算后续收益
                current_price = df['收盘'].values[i + LOOKBACK_DAYS - 1]  # 窗口最后一天收盘价
                future_price = df['收盘'].values[i + LOOKBACK_DAYS + PREDICT_DAYS - 1]  # 结束后第N天
                future_return = future_price / current_price - 1
                similarities.append({
                    'symbol': symbol,
                    'distance': dtw_distance,
                    'return': future_return,
                    'start_date': df.index[i],  # 形态开始日期
                    'end_date': df.index[i + LOOKBACK_DAYS - 1]  # 形态结束日期
                })
        except:
            continue
    # --------------------------结果分析--------------------------
    similar_df = pd.DataFrame(similarities)
    if similar_df.empty:
        print("未找到相似形态")
        return
    # 筛选前10%相似度案例
    top_samples = similar_df.nsmallest(int(len(similar_df) * 0.1), 'distance')
    print("\n=============== 分析结果 ===============")
    print(f"发现{len(top_samples)}个相似形态案例")
    print(f"未来{PREDICT_DAYS}日平均收益:{top_samples['return'].mean():.2%}")
    print(f"最大收益:{top_samples['return'].max():.2%}")
    print(f"最小收益:{top_samples['return'].min():.2%}")
    print("\n最具代表性股票:")
    print(top_samples.groupby('symbol').agg({
        'distance': 'mean',
        'return': 'mean'
    }).nsmallest(SIMILAR_NUM, 'distance'))
    # --------------------------结果可视化--------------------------
    best_case = top_samples.iloc[0]
    case_df = get_stock_data(best_case['symbol'])
    plt.figure(figsize=(12, 6))
    # 归一化显示
    plt.plot(np.linspace(0, 1, LOOKBACK_DAYS),
             scaler.fit_transform(target_prices.reshape(-1, 1)),
             label=f'目标形态 {TARGET_SYMBOL}', linewidth=2)
    best_window = case_df['收盘'].loc[best_case['start_date']:best_case['end_date']].values
    plt.plot(np.linspace(0, 1, LOOKBACK_DAYS),
             scaler.fit_transform(best_window.reshape(-1, 1)),
             '--',
             label=f'最佳匹配 {best_case["symbol"]} (收益:{best_case["return"]:.2%})')
    plt.title('股价形态匹配可视化')
    plt.xlabel('时间序列(归一化)')
    plt.ylabel('归一化价格')
    plt.legend()
    plt.grid(True)
    plt.show()
if __name__ == "__main__":
    main()

如果我的分享对您有所帮助,欢迎点赞转发。 您的支持和鼓励是我写作的动力。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 子晓聊技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 金融领域的典型应用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档