## 核心功能 ### 1. 成交量序列分析 (volume_price_sequence.py) - 按累计成交量排序的价格趋势分析 - 三合一综合图表:价格序列+成交量分布+时间序列 - 关键价格水平自动标注 ### 2. 成交量分布深度分析 (volume_distribution_analysis.py) - 7种专业可视化图表 - 统计特征分析和分布拟合 - 交易模式识别和业务洞察 ### 3. 大额订单分析工具集 (large_orders/) - 买1/卖1量大单分析 (阈值99) - 买卖挂单合计分析 (阈值200) - 当前成交量分析 (阈值150) - 信号抑制优化算法 (38%抑制率) ## 技术特性 - 信号抑制算法:有效减少重复信号干扰 - 多维度分析:支持多种信号类型 - 专业可视化:四宫格综合分析图 - 业务洞察:基于数据的交易建议 ## 分析结果 - 卖1量大单:短期下跌,长期大幅上涨反转 - 买挂合计:各时间窗口小幅正收益 - 信号抑制:短期收益从-0.0778提升至+0.1347 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
355 lines
14 KiB
Python
355 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
AU2512期货成交量在价格上的分布图
|
|
|
|
这个脚本专门用于生成当前成交量在价格上的分布图,
|
|
简单直观地展示不同价格水平下的成交量分布情况。
|
|
|
|
使用方法:
|
|
python volume_price_distribution.py [data_file]
|
|
|
|
如果不指定文件,默认分析 data/au2512_20251013.parquet
|
|
"""
|
|
|
|
import pandas as pd
|
|
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
import warnings
|
|
import os
|
|
import sys
|
|
import argparse
|
|
|
|
warnings.filterwarnings('ignore')
|
|
|
|
class VolumePriceDistributionAnalyzer:
|
|
"""成交量-价格分布分析器"""
|
|
|
|
def __init__(self, data_file=None):
|
|
"""
|
|
初始化分析器
|
|
|
|
Args:
|
|
data_file (str): 数据文件路径,默认为 data/au2512_20251013.parquet
|
|
"""
|
|
self.data_file = data_file or "data/au2512_20251013.parquet"
|
|
self.df = None
|
|
self.setup_chinese_font()
|
|
|
|
def setup_chinese_font(self):
|
|
"""设置中文字体支持"""
|
|
try:
|
|
# 直接使用已知可用的中文字体
|
|
chinese_fonts = ['Microsoft YaHei', 'SimHei', 'SimSun', 'KaiTi', 'FangSong', 'DengXian']
|
|
|
|
for font in chinese_fonts:
|
|
try:
|
|
plt.rcParams['font.sans-serif'] = [font]
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
plt.rcParams['font.size'] = 12
|
|
plt.rcParams['axes.titlesize'] = 16
|
|
plt.rcParams['axes.labelsize'] = 14
|
|
plt.rcParams['xtick.labelsize'] = 12
|
|
plt.rcParams['ytick.labelsize'] = 12
|
|
|
|
# 简单测试字体
|
|
fig, ax = plt.subplots(figsize=(1, 1))
|
|
ax.text(0.5, 0.5, '测试', fontsize=12)
|
|
plt.close(fig)
|
|
print(f"[*] 成功设置中文字体: {font}")
|
|
return
|
|
except:
|
|
continue
|
|
|
|
# 如果都失败,使用默认设置
|
|
print("[!] 无法设置中文字体,使用默认字体")
|
|
plt.rcParams['font.sans-serif'] = ['DejaVu Sans']
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
plt.rcParams['font.size'] = 12
|
|
|
|
except Exception as e:
|
|
print(f"[!] 字体设置警告: {e}")
|
|
plt.rcParams['font.sans-serif'] = ['DejaVu Sans']
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
plt.rcParams['font.size'] = 12
|
|
|
|
def load_data(self):
|
|
"""加载数据"""
|
|
try:
|
|
if not os.path.exists(self.data_file):
|
|
raise FileNotFoundError(f"数据文件不存在: {self.data_file}")
|
|
|
|
print(f"正在加载数据: {self.data_file}")
|
|
|
|
# 根据文件扩展名选择读取方式
|
|
if self.data_file.endswith('.parquet'):
|
|
self.df = pd.read_parquet(self.data_file)
|
|
elif self.data_file.endswith('.csv'):
|
|
self.df = pd.read_csv(self.data_file)
|
|
else:
|
|
raise ValueError("不支持的文件格式,请使用 .parquet 或 .csv 文件")
|
|
|
|
print(f"数据加载成功: {len(self.df):,} 条记录")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"数据加载失败: {e}")
|
|
return False
|
|
|
|
def prepare_data(self):
|
|
"""准备分析数据"""
|
|
print("\n=== 准备价格-成交量分布数据 ===")
|
|
|
|
# 获取列名
|
|
price_col = None
|
|
cumulative_volume_col = None
|
|
|
|
# 自动检测关键列
|
|
for i, col in enumerate(self.df.columns):
|
|
if '成交价' in col and price_col is None:
|
|
price_col = col
|
|
elif cumulative_volume_col is None and i == 3: # 累计成交量通常在第4列
|
|
cumulative_volume_col = col
|
|
|
|
if price_col is None or cumulative_volume_col is None:
|
|
raise ValueError("无法找到成交价或累计成交量列")
|
|
|
|
print(f"使用价格列: {price_col}")
|
|
print(f"使用累计成交量列: {cumulative_volume_col}")
|
|
|
|
# 准备分析数据
|
|
analysis_data = self.df[[price_col, cumulative_volume_col]].copy()
|
|
analysis_data.columns = ['价格', '累计成交量']
|
|
|
|
# 计算当前成交量
|
|
analysis_data['当前成交量'] = analysis_data['累计成交量'].diff().fillna(0)
|
|
analysis_data.loc[analysis_data.index[0], '当前成交量'] = analysis_data.loc[analysis_data.index[0], '累计成交量']
|
|
|
|
# 移除零值和异常值
|
|
valid_data = analysis_data[
|
|
(analysis_data['当前成交量'] > 0) &
|
|
(analysis_data['当前成交量'] < analysis_data['当前成交量'].quantile(0.999))
|
|
].copy()
|
|
|
|
print(f"原始数据点: {len(analysis_data):,}")
|
|
print(f"有效数据点: {len(valid_data):,}")
|
|
print(f"价格范围: {valid_data['价格'].min():.2f} - {valid_data['价格'].max():.2f}")
|
|
print(f"成交量范围: {valid_data['当前成交量'].min():.2f} - {valid_data['当前成交量'].max():.2f} 手")
|
|
print(f"价格间隔: 0.02 元/格")
|
|
|
|
self.analysis_data = valid_data
|
|
return True
|
|
|
|
def create_volume_distribution_chart(self):
|
|
"""创建成交量在价格上的分布图"""
|
|
print("\n=== 生成成交量价格分布图 ===")
|
|
|
|
# 创建图表
|
|
plt.figure(figsize=(16, 10))
|
|
|
|
# 创建价格分组
|
|
prices = self.analysis_data['价格']
|
|
min_price = prices.min()
|
|
max_price = prices.max()
|
|
|
|
# 使用固定0.02价格间隔
|
|
interval_size = 0.02
|
|
price_bins = np.arange(np.floor(min_price * 50) / 50, # 向下取整到0.02的倍数
|
|
np.ceil(max_price * 50) / 50 + interval_size, # 向上取整
|
|
interval_size)
|
|
bin_centers = (price_bins[:-1] + price_bins[1:]) / 2
|
|
|
|
# 计算每个价格分组的成交量
|
|
volume_by_price = []
|
|
for i in range(len(price_bins) - 1):
|
|
mask = (self.analysis_data['价格'] >= price_bins[i]) & (self.analysis_data['价格'] < price_bins[i + 1])
|
|
volume_sum = self.analysis_data.loc[mask, '当前成交量'].sum()
|
|
volume_by_price.append(volume_sum)
|
|
|
|
volume_by_price = np.array(volume_by_price)
|
|
|
|
# 绘制成交量分布柱状图
|
|
colors = plt.cm.RdYlBu_r(np.linspace(0, 1, len(bin_centers)))
|
|
bars = plt.bar(bin_centers, volume_by_price,
|
|
width=(price_bins[1] - price_bins[0]) * 0.8,
|
|
color=colors, alpha=0.8, edgecolor='black', linewidth=0.5)
|
|
|
|
# 设置标题和标签
|
|
plt.title('AU2512期货成交量在价格上的分布 (价格间隔: 0.02)', fontsize=20, fontweight='bold', pad=20)
|
|
plt.xlabel('价格', fontsize=16, labelpad=10)
|
|
plt.ylabel('累计成交量 (手)', fontsize=16, labelpad=10)
|
|
plt.grid(True, alpha=0.3, axis='y')
|
|
|
|
# 找出成交量最大的几个价格区间
|
|
top_indices = np.argsort(volume_by_price)[-5:] # 取前5个
|
|
|
|
# 标注成交量最大的价格区间
|
|
for i in top_indices:
|
|
if volume_by_price[i] > 0:
|
|
plt.annotate(f'{volume_by_price[i]:.0f}手',
|
|
xy=(bin_centers[i], volume_by_price[i]),
|
|
xytext=(0, 20), textcoords='offset points',
|
|
ha='center', va='bottom',
|
|
fontsize=11, fontweight='bold', color='red',
|
|
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.8),
|
|
arrowprops=dict(arrowstyle='->', color='red', alpha=0.7))
|
|
|
|
# 添加统计信息
|
|
max_volume = volume_by_price.max()
|
|
max_price_range = price_bins[np.argmax(volume_by_price)]
|
|
max_price_range_end = price_bins[np.argmax(volume_by_price) + 1]
|
|
|
|
total_volume = volume_by_price.sum()
|
|
max_volume_percentage = (max_volume / total_volume) * 100
|
|
|
|
stats_text = f'''统计信息:
|
|
• 价格区间: {min_price:.2f} - {max_price:.2f}
|
|
• 总成交量: {total_volume:,.0f} 手
|
|
• 最大成交量: {max_volume:,.0f} 手 ({max_volume_percentage:.1f}%)
|
|
• 最活跃价格区间: {max_price_range:.2f} - {max_price_range_end:.2f}'''
|
|
|
|
plt.text(0.98, 0.98, stats_text, transform=plt.gca().transAxes,
|
|
fontsize=12, ha='right', va='top',
|
|
bbox=dict(boxstyle='round,pad=0.5', facecolor='lightgreen', alpha=0.9))
|
|
|
|
# 格式化y轴标签
|
|
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter(lambda x, p: f'{x/1000:.0f}K'))
|
|
|
|
# 调整布局
|
|
plt.tight_layout()
|
|
|
|
# 保存图表
|
|
chart_file = 'au2512_volume_price_distribution.png'
|
|
plt.savefig(chart_file, dpi=300, bbox_inches='tight',
|
|
facecolor='white', edgecolor='none')
|
|
plt.close()
|
|
|
|
print(f"成交量价格分布图已保存: {chart_file}")
|
|
return chart_file
|
|
|
|
def print_summary(self):
|
|
"""打印分析摘要"""
|
|
print("\n" + "="*60)
|
|
print("成交量在价格上分布分析摘要")
|
|
print("="*60)
|
|
|
|
prices = self.analysis_data['价格']
|
|
volumes = self.analysis_data['当前成交量']
|
|
|
|
print(f"\n【数据概况】")
|
|
print(f"数据文件: {self.data_file}")
|
|
print(f"有效记录数: {len(self.analysis_data):,}")
|
|
print(f"价格范围: {prices.min():.2f} - {prices.max():.2f}")
|
|
print(f"成交量范围: {volumes.min():.2f} - {volumes.max():.2f} 手")
|
|
print(f"平均成交量: {volumes.mean():.2f} 手")
|
|
|
|
# 创建价格分组的详细数据
|
|
min_price = prices.min()
|
|
max_price = prices.max()
|
|
interval_size = 0.02
|
|
|
|
price_bins = np.arange(np.floor(min_price * 50) / 50,
|
|
np.ceil(max_price * 50) / 50 + interval_size,
|
|
interval_size)
|
|
|
|
bin_centers = (price_bins[:-1] + price_bins[1:]) / 2
|
|
|
|
# 计算每个价格分组的成交量
|
|
volume_by_price = []
|
|
for i in range(len(price_bins) - 1):
|
|
mask = (self.analysis_data['价格'] >= price_bins[i]) & (self.analysis_data['价格'] < price_bins[i + 1])
|
|
volume_sum = self.analysis_data.loc[mask, '当前成交量'].sum()
|
|
volume_by_price.append(volume_sum)
|
|
|
|
volume_by_price = np.array(volume_by_price)
|
|
total_volume = volume_by_price.sum()
|
|
|
|
# 找出成交量最大的价格区间
|
|
max_idx = np.argmax(volume_by_price)
|
|
max_volume = volume_by_price[max_idx]
|
|
max_price_start = price_bins[max_idx]
|
|
max_price_end = price_bins[max_idx + 1]
|
|
|
|
print(f"\n【最活跃价格区间】")
|
|
print(f"价格区间: {max_price_start:.2f} - {max_price_end:.2f}")
|
|
print(f"区间成交量: {max_volume:,.0f} 手")
|
|
print(f"占总成交量比例: {(max_volume/total_volume*100):.1f}%")
|
|
|
|
# 列出前20个成交最大的价格区间
|
|
print(f"\n【前20个成交最大的价格区间】")
|
|
print(f"{'排名':>4} {'价格区间':>15} {'成交量':>12} {'占比':>8} {'累计占比':>10}")
|
|
print("-" * 65)
|
|
|
|
# 获取排序后的索引
|
|
sorted_indices = np.argsort(volume_by_price)[::-1] # 降序排列
|
|
|
|
cumulative_percentage = 0
|
|
for rank in range(min(20, len(sorted_indices))):
|
|
idx = sorted_indices[rank]
|
|
if volume_by_price[idx] > 0: # 只显示有成交量的区间
|
|
price_start = price_bins[idx]
|
|
price_end = price_bins[idx + 1]
|
|
volume = volume_by_price[idx]
|
|
percentage = (volume / total_volume) * 100
|
|
cumulative_percentage += percentage
|
|
|
|
print(f"{rank+1:>4} {price_start:>7.2f}-{price_end:<7.2f} {volume:>10,.0f} {percentage:>6.1f}% {cumulative_percentage:>8.1f}%")
|
|
|
|
print(f"\n【生成的图表】")
|
|
print(f" au2512_volume_price_distribution.png - 成交量价格分布图")
|
|
|
|
print(f"\n" + "="*60)
|
|
print("分析完成!")
|
|
print("="*60)
|
|
|
|
def run_analysis(self):
|
|
"""运行完整分析"""
|
|
print("开始AU2512期货成交量价格分布分析...")
|
|
|
|
# 加载数据
|
|
if not self.load_data():
|
|
return False
|
|
|
|
# 准备数据
|
|
if not self.prepare_data():
|
|
return False
|
|
|
|
# 创建图表
|
|
self.create_volume_distribution_chart()
|
|
|
|
# 打印摘要
|
|
self.print_summary()
|
|
|
|
return True
|
|
|
|
def main():
|
|
"""主函数"""
|
|
parser = argparse.ArgumentParser(description='AU2512期货成交量价格分布分析工具')
|
|
parser.add_argument('data_file', nargs='?',
|
|
default='data/au2512_20251013.parquet',
|
|
help='数据文件路径 (默认: data/au2512_20251013.parquet)')
|
|
parser.add_argument('--output-dir', '-o', default='.',
|
|
help='输出目录 (默认: 当前目录)')
|
|
|
|
args = parser.parse_args()
|
|
|
|
# 检查数据文件是否存在
|
|
if not os.path.exists(args.data_file):
|
|
print(f"错误: 数据文件不存在 - {args.data_file}")
|
|
print("\n使用方法:")
|
|
print(" python volume_price_distribution.py [数据文件路径]")
|
|
print(f" python volume_price_distribution.py # 使用默认文件: {args.data_file}")
|
|
sys.exit(1)
|
|
|
|
# 创建分析器并运行
|
|
analyzer = VolumePriceDistributionAnalyzer(args.data_file)
|
|
success = analyzer.run_analysis()
|
|
|
|
if success:
|
|
print(f"\n分析完成!图表已保存到: {args.output_dir}")
|
|
else:
|
|
print(f"\n分析失败!")
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
main() |