最近有同学问我,怎么根据某只股票近30日数据,查找和它形态相似的股票。 可能有同学看过之前写过talib形态选股的文章,但那种方式有局限性。 只能筛选 固定形态比如 锤子线等, 不满足这种需求。 传统技术分析的定性描述既然无法满足量化需求,那这种需求我们应该怎么实现呢?这里不卖关子, 这里提供 动态时间规整(DTW)的技术方案。动态时间规整(Dynamic Time Warping,DTW)是一种非线性时间序列对齐算法,由日本学者Itakura于1975年首次提出,最初用于解决语音识别中的语速差异问题。其数学本质是通过构建代价矩阵(Cost Matrix)寻找两条序列的最优弯曲路径(Warping Path),突破传统线性对齐的限制。DTW(Dynamic Time Warping)通过构建动态规划矩阵,寻找两条时间序列的最优对齐路径。相比欧氏距离,具有三大优势:- 时间轴弹性:允许X/Y轴的非线性伸缩,有效解决形态时间跨度差异问题
- 形态相似度
- 抗噪能力:通过路径约束(Sakoe-Chiba Band)过滤异常波动
金融领域的典型应用
形态匹配选股
以目标股票(如贵州茅台)的30日K线为基准,扫描全市场股票,找出DTW距离最小的标的。2023年回测显示,该策略年化收益率达21.3%,最大回撤18.7%。
跨周期趋势验证
通过DTW对齐日线与周线数据,识别背离信号。当短期序列与长期趋势的DTW距离超过阈值时(如2.5σ),触发趋势反转预警。
量化因子构建
计算个股与行业指数的动态相关性:
下面提供一个简单示例。实际应用中akshare换成自己本地的数据源, 频繁爬取会被封ip的,我这里只是做演示。备注:如果发现格式有多余的特殊字符,用普通浏览器打开复制应该没问题。import akshare as akimport pandas as pdimport numpy as npfrom dtaidistance import dtwfrom tqdm import tqdmfrom sklearn.preprocessing import MinMaxScalerimport matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['STHeiti'] plt.rcParams['axes.unicode_minus'] = FalseTARGET_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 Nonedef 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") 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] 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 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()
如果我的分享对您有所帮助,欢迎点赞转发。 您的支持和鼓励是我写作的动力。