huice/volume_price_distribution.py
Your Name e5dd5b5593 feat: 期货数据分析工具集 v2.0
## 核心功能
### 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>
2025-11-02 15:15:53 +08:00

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()