核心改进: - 新增 converging_triangle_optimized.py,使用Numba JIT编译优化7个核心函数 - 在 converging_triangle.py 末尾自动导入优化版本,无需手动配置 - 全量检测耗时从30秒降至<1秒(首次需3-5秒编译) 性能提升明细: - pivots_fractal: 460x 加速 - pivots_fractal_hybrid: 511x 加速 - fit_boundary_anchor: 138x 加速 - calc_boundary_utilization: 195x 加速 - calc_fitting_adherence: 7x 加速 - calc_breakout_strength: 3x 加速 绘图功能增强: - 添加 --plot-boundary-source 参数,支持选择高低价或收盘价拟合边界线 - 默认改为使用收盘价拟合(更平滑、更符合实际交易) - 添加 --show-high-low 参数,可选显示日内高低价范围 技术特性: - 自动检测并启用Numba加速,无numba时自动降级 - 结果与原版100%一致(误差<1e-6) - 完整的性能测试和对比验证 - 零侵入性,原版函数作为备用 新增文件: - src/converging_triangle_optimized.py - Numba优化版核心函数 - docs/README_性能优化.md - 性能优化文档索引 - docs/性能优化执行总结.md - 快速参考 - docs/性能优化完整报告.md - 完整技术报告 - docs/性能优化方案.md - 详细技术方案 - scripts/test_performance.py - 性能基线测试 - scripts/test_optimization_comparison.py - 优化对比测试 - scripts/test_full_pipeline.py - 完整流水线测试 - scripts/README_performance_tests.md - 测试脚本使用说明 修改文件: - README.md - 添加性能优化说明和依赖 - src/converging_triangle.py - 集成优化版本导入 - scripts/pipeline_converging_triangle.py - 默认使用收盘价拟合 - scripts/plot_converging_triangles.py - 默认使用收盘价拟合
304 lines
10 KiB
Python
304 lines
10 KiB
Python
"""
|
||
收敛三角形检测流水线
|
||
|
||
一键执行完整的检测、报告生成和图表绘制流程:
|
||
1. 运行批量检测 (run_converging_triangle.py)
|
||
2. 生成选股报告 (report_converging_triangles.py)
|
||
3. 绘制个股图表 (plot_converging_triangles.py)
|
||
|
||
用法:
|
||
python scripts/pipeline_converging_triangle.py
|
||
python scripts/pipeline_converging_triangle.py --date 20260120
|
||
python scripts/pipeline_converging_triangle.py --show-details # 生成详情模式图片
|
||
python scripts/pipeline_converging_triangle.py --plot-boundary-source close # 使用收盘价拟合边界线
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import os
|
||
import sys
|
||
import time
|
||
from datetime import datetime
|
||
|
||
# 让脚本能找到 src/ 下的模块
|
||
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "src"))
|
||
|
||
# 导入三个主要模块
|
||
from run_converging_triangle import main as run_detection
|
||
from report_converging_triangles import main as run_report
|
||
from plot_converging_triangles import main as run_plot
|
||
from generate_stock_viewer import main as run_viewer
|
||
|
||
|
||
def print_section_header(title: str, step: int) -> None:
|
||
"""打印流程步骤标题"""
|
||
print("\n")
|
||
print("=" * 80)
|
||
print(f"步骤 {step}: {title}")
|
||
print("=" * 80)
|
||
print()
|
||
|
||
|
||
def print_step_result(success: bool, duration: float) -> None:
|
||
"""打印步骤执行结果"""
|
||
status = "[完成]" if success else "[失败]"
|
||
print()
|
||
print("-" * 80)
|
||
print(f"{status} | 耗时: {duration:.2f} 秒 ({duration/60:.2f} 分钟)")
|
||
print("-" * 80)
|
||
|
||
|
||
def main() -> None:
|
||
parser = argparse.ArgumentParser(description="收敛三角形检测完整流水线")
|
||
parser.add_argument(
|
||
"--date",
|
||
type=int,
|
||
default=None,
|
||
help="指定日期(YYYYMMDD),用于报告和图表生成(默认为数据最新日)",
|
||
)
|
||
parser.add_argument(
|
||
"--show-details",
|
||
action="store_true",
|
||
help="生成详情模式图片(显示所有枢轴点和拟合点)",
|
||
)
|
||
parser.add_argument(
|
||
"--skip-detection",
|
||
action="store_true",
|
||
help="跳过批量检测步骤(如果已有检测结果)",
|
||
)
|
||
parser.add_argument(
|
||
"--skip-report",
|
||
action="store_true",
|
||
help="跳过报告生成步骤",
|
||
)
|
||
parser.add_argument(
|
||
"--skip-plot",
|
||
action="store_true",
|
||
help="跳过图表绘制步骤",
|
||
)
|
||
parser.add_argument(
|
||
"--all-stocks",
|
||
action="store_true",
|
||
help="为所有108只股票生成图表(包括不满足收敛三角形条件的)",
|
||
)
|
||
parser.add_argument(
|
||
"--skip-viewer",
|
||
action="store_true",
|
||
help="跳过生成HTML查看器",
|
||
)
|
||
parser.add_argument(
|
||
"--clean",
|
||
action="store_true",
|
||
help="运行前清空outputs文件夹",
|
||
)
|
||
parser.add_argument(
|
||
"--plot-boundary-source",
|
||
choices=["hl", "close"],
|
||
default="close",
|
||
help="绘图时边界线拟合数据源: hl=高低价, close=收盘价(不影响检测)",
|
||
)
|
||
args = parser.parse_args()
|
||
|
||
pipeline_start = time.time()
|
||
|
||
print("=" * 80)
|
||
print("收敛三角形检测流水线")
|
||
print("=" * 80)
|
||
print(f"开始时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
if args.date:
|
||
print(f"指定日期: {args.date}")
|
||
if args.show_details:
|
||
print(f"图表模式: 详情模式(显示所有枢轴点)")
|
||
else:
|
||
print(f"图表模式: 简洁模式(仅显示价格和趋势线)")
|
||
if args.all_stocks:
|
||
print(f"图表范围: 所有108只股票(包括不满足条件的)")
|
||
else:
|
||
print(f"图表范围: 仅满足收敛三角形条件的股票")
|
||
boundary_source_name = "收盘价" if args.plot_boundary_source == "close" else "高低价"
|
||
print(f"边界线拟合数据源: {boundary_source_name}")
|
||
print("=" * 80)
|
||
|
||
# ========================================================================
|
||
# 步骤 0: 清空输出目录(可选)
|
||
# ========================================================================
|
||
if args.clean:
|
||
import shutil
|
||
output_base = os.path.join(os.path.dirname(__file__), "..", "outputs", "converging_triangles")
|
||
if os.path.exists(output_base):
|
||
print("\n[清空输出目录]")
|
||
print(f" 删除: {output_base}")
|
||
shutil.rmtree(output_base)
|
||
os.makedirs(output_base, exist_ok=True)
|
||
print(" [OK] 输出目录已清空\n")
|
||
|
||
results = []
|
||
|
||
# ========================================================================
|
||
# 步骤 1: 批量检测
|
||
# ========================================================================
|
||
if not args.skip_detection:
|
||
print_section_header("批量检测 - 识别所有收敛三角形形态", 1)
|
||
step_start = time.time()
|
||
|
||
try:
|
||
# 清除命令行参数,避免冲突
|
||
sys.argv = [sys.argv[0]]
|
||
run_detection()
|
||
success = True
|
||
except Exception as e:
|
||
print(f"\n❌ 检测失败: {e}")
|
||
success = False
|
||
|
||
step_duration = time.time() - step_start
|
||
print_step_result(success, step_duration)
|
||
results.append(("批量检测", success, step_duration))
|
||
|
||
if not success:
|
||
print("\n[流水线中断:批量检测失败]")
|
||
return
|
||
else:
|
||
print("\n[跳过批量检测步骤(使用已有结果)]")
|
||
results.append(("批量检测", None, 0))
|
||
|
||
# ========================================================================
|
||
# 步骤 2: 生成报告
|
||
# ========================================================================
|
||
if not args.skip_report:
|
||
print_section_header("生成报告 - 当日选股简报", 2)
|
||
step_start = time.time()
|
||
|
||
try:
|
||
# 设置命令行参数
|
||
if args.date:
|
||
sys.argv = [sys.argv[0], "--report-date", str(args.date)]
|
||
else:
|
||
sys.argv = [sys.argv[0]]
|
||
|
||
run_report()
|
||
success = True
|
||
except Exception as e:
|
||
print(f"\n❌ 报告生成失败: {e}")
|
||
success = False
|
||
|
||
step_duration = time.time() - step_start
|
||
print_step_result(success, step_duration)
|
||
results.append(("生成报告", success, step_duration))
|
||
|
||
if not success:
|
||
print("\n[报告生成失败,但继续执行图表绘制]")
|
||
else:
|
||
print("\n[跳过报告生成步骤]")
|
||
results.append(("生成报告", None, 0))
|
||
|
||
# ========================================================================
|
||
# 步骤 3: 绘制图表
|
||
# ========================================================================
|
||
if not args.skip_plot:
|
||
print_section_header("绘制图表 - 个股收敛三角形可视化", 3)
|
||
step_start = time.time()
|
||
|
||
try:
|
||
# 设置命令行参数
|
||
cmd_args = [sys.argv[0]]
|
||
if args.date:
|
||
cmd_args.extend(["--date", str(args.date)])
|
||
if args.show_details:
|
||
cmd_args.append("--show-details")
|
||
if args.all_stocks:
|
||
cmd_args.append("--all-stocks")
|
||
if args.plot_boundary_source:
|
||
cmd_args.extend(["--plot-boundary-source", args.plot_boundary_source])
|
||
|
||
sys.argv = cmd_args
|
||
|
||
run_plot()
|
||
success = True
|
||
except Exception as e:
|
||
print(f"\n❌ 图表绘制失败: {e}")
|
||
success = False
|
||
|
||
step_duration = time.time() - step_start
|
||
print_step_result(success, step_duration)
|
||
results.append(("绘制图表", success, step_duration))
|
||
else:
|
||
print("\n[跳过图表绘制步骤]")
|
||
results.append(("绘制图表", None, 0))
|
||
|
||
# ========================================================================
|
||
# 步骤 4: 生成HTML查看器
|
||
# ========================================================================
|
||
if not args.skip_viewer:
|
||
print_section_header("生成HTML查看器 - 可视化强度分筛选", 4)
|
||
step_start = time.time()
|
||
|
||
try:
|
||
# 设置命令行参数
|
||
cmd_args = [sys.argv[0]]
|
||
if args.date:
|
||
cmd_args.extend(["--date", str(args.date)])
|
||
if args.all_stocks:
|
||
cmd_args.append("--all-stocks")
|
||
|
||
sys.argv = cmd_args
|
||
|
||
run_viewer()
|
||
success = True
|
||
except Exception as e:
|
||
print(f"\n❌ HTML查看器生成失败: {e}")
|
||
success = False
|
||
|
||
step_duration = time.time() - step_start
|
||
print_step_result(success, step_duration)
|
||
results.append(("生成查看器", success, step_duration))
|
||
else:
|
||
print("\n[跳过HTML查看器生成步骤]")
|
||
results.append(("生成查看器", None, 0))
|
||
|
||
# ========================================================================
|
||
# 流水线总结
|
||
# ========================================================================
|
||
pipeline_duration = time.time() - pipeline_start
|
||
|
||
print("\n")
|
||
print("=" * 80)
|
||
print("流水线执行总结")
|
||
print("=" * 80)
|
||
|
||
for step_name, success, duration in results:
|
||
if success is None:
|
||
status = "[跳过]"
|
||
time_str = "-"
|
||
elif success:
|
||
status = "[成功]"
|
||
time_str = f"{duration:.2f}s"
|
||
else:
|
||
status = "[失败]"
|
||
time_str = f"{duration:.2f}s"
|
||
|
||
print(f"{step_name:12} | {status:8} | {time_str:>10}")
|
||
|
||
print("-" * 80)
|
||
print(f"总耗时: {pipeline_duration:.2f} 秒 ({pipeline_duration/60:.2f} 分钟)")
|
||
print(f"结束时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
|
||
# 检查是否所有步骤都成功
|
||
all_success = all(s is None or s for _, s, _ in results)
|
||
|
||
if all_success:
|
||
print("\n[流水线执行完成]")
|
||
print("\n输出文件:")
|
||
print(" - outputs/converging_triangles/all_results.csv")
|
||
print(" - outputs/converging_triangles/report.md")
|
||
print(" - outputs/converging_triangles/charts/*.png")
|
||
print(" - outputs/converging_triangles/stock_viewer.html ← 用浏览器打开")
|
||
else:
|
||
print("\n[流水线部分失败,请检查上述错误信息]")
|
||
|
||
print("=" * 80)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|