PyTrendline Research Report

样本:BTC-USD | 10d / 5m | 生成时间:2026-03-12 08:42 UTC

这次落地了什么

这个区块回答:这页相较于普通“结果页”,额外补了哪些 explainability 信息。

四段式结构导航

这页现在按四段式结构组织:先定义,再计算,再看结果,最后单独收束边界与下一步。这样可以减少“定义、结果、限制”来回穿插造成的阅读负担。

1. 定义层:对象、参数、来源、读法 2. 计算层:pivot → candidate → filter → grouping 3. 结果层:图、代表线、事件线、总览 4. 边界层:time semantics / research-only / next step

第一层:定义层

先统一读法、输入边界、参数选择与图上元素语义。这个层只回答“这页在看什么、怎么读、对象分别是什么”。

页面导读 / Reading guide

这个区块回答:这页应该按什么顺序看,哪些部分是“定义表”,哪些部分是“结果图/结果表”。

step how_to_read why_it_matters
1 先看“页面导读”与“步骤总览” 先建立这页的阅读顺序:数据从哪来、经过哪些中间层、最后展示什么。
2 再看参数 / 窗口 / 来源边界 先确认这次到底扫了什么样本、为什么只看最近窗口、这页能回答什么不能回答什么。
3 再看 K 线 + pivots 先看结构锚点,再理解后面的 trendline 不是凭空长出来的。
4 然后看 support / resistance / breakout 图 把线和锚点、事件线分开看,避免一上来就被最终总览图淹没。
5 最后看表格与边界 先用图形成直觉,再用字段解释、代表线表和边界卡片收束理解。

步骤总览:从输入到结果

这个区块回答:数据是怎样一步步从 bars 走到 pivots、再走到代表线和 breakout 标签的。

stage object what_happens_here
1 bars 下载 period × interval 的 OHLCV,再裁成最近 `window_bars` 根。
2 pivots 在窗口内识别 support / resistance pivots,作为结构锚点。
3 candidate lines 从 pivots 组合中尝试构造候选趋势线。
4 filtered lines 受 `min_points_required`、pivot 约束、breakout 约束等规则筛掉不合格线。
5 duplicate groups 把非常接近的线分组,避免图上同类线过度堆叠。
6 best lines + breakout labels 保留更适合展示的代表线,并标出哪些线已经被识别成 breakout。

参数

这个区块回答:这次扫描到底看了什么数据、用了哪些关键参数。

parameter value
ticker BTC-USD
period 10d
interval 5m
window_bars 96
min_points_required 3
ignore_breakouts False
all_pts_must_be_pivots True

窗口 / 运行边界

这个区块回答:为什么当前只看最近窗口,以及这样做的代价和边界是什么。

concept operational_definition
period 从数据源下载多长历史,用于拿到候选样本池。
interval 单根 bar 的时间粒度;它决定 pivot 密度、斜率尺度与图上噪声水平。
window_bars 真正送进 pytrendline 扫描的最近窗口长度;当前报告只对最近窗口做研究,而不是对整段历史全量扫描。
为什么只看最近窗口 因为 pytrendline 是高复杂度穷举搜索;最近窗口更适合 inspection / explainability,而不是实时全量引擎。

来源与边界

这个区块回答:为什么选 pytrendline、为什么不直接搬别的实现、以及这页当前的定位是什么。

question answer
为什么不直接搬 PyIndicators breakout navigator 因为该文件来源带 LuxAlgo / CC BY-NC-SA 4.0 / analytical use only 语义,代码来源边界不够干净。
为什么这次选 pytrendline MIT,且原生就解决 pivot → support/resistance trendlines → breakout 研究这条链。
当前定位 研究功能,不是正式交易信号。
当前边界 只在最近一个窗口扫描,因为 pytrendline 是 O(N^3) 级别的穷举趋势线搜索。

为什么当前参数这样设

这个区块回答:`96 / 3 / pivot-only / breakout-on` 这些选择不是随意的,它们各自想在“解释力、复杂度、可读性”之间取什么平衡。

parameter_choice operational_meaning why_this_is_current_default
window_bars = 96 在 5m 粒度下约等于最近 8 小时。 比 48 更不容易只看到局部噪声,比 144 更能控制 O(N^3) 穷举成本与页面可读性。
min_points_required = 3 要求一条线至少命中 3 个结构点。 比 2 点连线更严格;2 点几乎总能画线,但解释力偏弱,3 点更适合作为研究报告里的代表线基线。
all_pts_must_be_pivots = True 要求命中的点都来自已识别的 pivots。 牺牲部分灵活性,换来更清晰的结构语义;这样页面里的线更容易和锚点一一对照。
ignore_breakouts = False 保留 breakout 检测,而不是只保留静态结构线。 这样报告既能展示结构线,也能区分哪些线已经被标成事件线,方便后续研究 breakout 语义。

图上元素字典

这个区块回答:图里每个颜色、线型、标记各代表什么,方便和后面的图一一对照。

chart_element meaning
绿色 K 线实体 收盘价 >= 开盘价的上涨 bar。
红色 K 线实体 收盘价 < 开盘价的下跌 bar。
灰色上下影线 该 bar 的 high / low 范围。
青绿色倒三角 support pivots,表示支撑侧结构锚点。
橙色正三角 resistance pivots,表示阻力侧结构锚点。
三角标旁的小数字 对应该 pivot 的 bar index,让 Step 1 图能和 deep-dive / best lines 表里的 pivot index 直接对上。
蓝色实线 support best lines(未标成 breakout 的代表支撑线)。
紫色实线 resistance best lines(未标成 breakout 的代表阻力线)。
蓝/紫实心圆点 当前展示线命中的结构点(pointset pivots);点内数字是对应的 pivot index,用来说明这条线到底是被哪些点支撑出来的。
绿色虚线 support breakout lines:已被 pytrendline 标为 breakout 的支撑线。
红色虚线 resistance breakout lines:已被 pytrendline 标为 breakout 的阻力线。

第二层:计算层

这一层回答结构对象是怎样被算出来的:从 pivots、candidate pairs、过滤、duplicate grouping,一直到 breakout 标签与 lifecycle。

逐步骤操作性定义

这个区块回答:每一步“具体在算什么”,避免把结果页误读成纯视觉画线。

step name operational_definition
1 取 bars 先下载 `period × interval` 的 OHLCV,再只保留最近 `window_bars` 根。
2 找 pivots pytrendline 在窗口内给出 support pivots / resistance pivots;它们是原始 K 线高低点层面的结构锚点。
3 枚举候选线 从 pivots 组合中尝试构造 support / resistance trendlines。
4 筛选合法线 受 `min_points_required`、pivot 约束、breakout 约束等条件过滤。
5 重复线分组 把非常相似的线放在一起,保留每组 best line 便于阅读。
6 标记 breakout 对最终线判断是否已发生 breakout;当前更偏研究标记,不直接等价于可交易信号。

Pivot points 是怎么来的

这个区块回答:support / resistance pivots 在源码里到底是怎么选出来的,以及这为什么会影响 bar-by-bar 可用性。

rule current_window_value_or_logic why_it_matters
support pivots 看哪里 在原始 `Low` 序列上找局部低点。 不是平滑曲线上的点,而是窗口内原始 K 线低点。
resistance pivots 看哪里 在原始 `High` 序列上找局部高点。 同样直接使用原始 K 线高点。
grouping threshold ≈ 6.4727 若相邻若干根高/低点差异很小,会先被视作一组近似连续 pivots,再拿更远的前后点比较。
separation threshold ≈ 12.9455 候选 pivot 需要和前后比较点拉开足够距离,避免把太小的局部抖动也当成结构点。
局部极值条件 support 要比前后低;resistance 要比前后高。 源码里先检查局部高/低,再检查是否达到 separation 要求。
首尾 bar 窗口的 first / last index 总会被纳入 pivots。 这样趋势线搜索不会完全失去窗口边界上的锚点。
bar-by-bar 边界 pivot 判断会看右侧若干根 bar。 因此它更适合研究解释;若要实时使用,还需要额外审计“何时才算确认”这件事。

候选趋势线是怎么从 pivots 组合出来的

这个区块回答:是不是所有 pivot 组合都会尝试、它们经历了哪些过滤、以及为什么最后页面里只剩下一小部分线。

question current_answer why_it_matters
起点/终点从哪来 在当前配置下,start/end 都必须来自 pivots。 因为 `all_pts_must_be_pivots=True`,所以非 pivot 的 i/j 组合会直接跳过。
support 候选对数量(理论上限) 253 这是当前窗口里 support pivots 两两配对的理论上限。
resistance 候选对数量(理论上限) 231 这是当前窗口里 resistance pivots 两两配对的理论上限。
每个候选对先做什么 先用两个点拟合一条直线(`m`, `b`)。 源码对每个 i
第一层过滤 先过滤 slope 与 last-price 不合理的线。 避免生成方向/位置明显不合要求的候选线。
第二层过滤 再扫描从起点到窗口末尾的 bars,用 `max_allowable_error ≈ 3.8836` 统计有多少点贴近这条线。 只有足够多的点贴着线,这条线才算有结构支撑。
最小命中数 至少需要 `num_points >= 3`。 当前默认值是 3,因此只有 2 点撑起来的线不会进入最终结果。
breakout 检测 如果价格越过线并超过 `breakout_tolerance ≈ 5.1782`,就会标记 breakout。 这一步决定它只是结构线,还是已经被标成事件线。
去重前的重复来源 不同 pivot 对可能收敛到几乎同一条线。 所以后面还需要 duplicate grouping,而不是把所有近似重合线都展示出来。
当前窗口最终保留 support all=133,resistance all=95 这是通过最小命中数 / slope / last-price / breakout 规则后留下来的全部候选结果数。

Candidate lines before filtering:原始 pivot-pair 候选线有多密

这个区块回答:在任何 slope / last-price / num_points / duplicate grouping 过滤生效之前,单靠 pivot-pair 组合会产生多密的原始候选线。这里展示的是示意性抽样 / 截断视图,目的是让读者直观看到“候选线爆炸”而不是把它误读成最终线集合。

candidate lines before filtering

图下注释:每条浅色线段都只是某一对 pivots 直接定义出来的原始候选段,还没有经过 slope 合法性、last-price 合法性、最小命中点数与 duplicate grouping 压缩。它的作用是帮助理解:为什么页面最后只显示少数代表线,而不是把所有 pivot 两两相连后都堆出来。

为什么不能把所有 pivot 两两相连

这个区块回答:为什么“把所有 pivot 都连起来”会失真,以及当前代码到底用了哪几层规则来压缩这些候选线。

question current_answer
为什么不能把所有 pivot 两两相连后都直接展示 因为 2 点连线天然太多,而且很多线只是偶然穿过两个点,并不具备足够结构支撑。
当前先过滤掉什么 先过滤 slope 不合法、最后价格位置明显不合理的候选线。
真正让一条线“像趋势线”的关键是什么 当前至少要求 `num_points >= 3`,也就是除了起终点外,还要有更多价格点贴近这条线。
为什么最后还要 duplicate grouping 因为不同 pivot 对可能生成几乎重合的线;若不分组,页面会被视觉上重复的线淹没。

从 pivot 到 trendline:决策链总览

这个区块回答:pivot collection / pair enumeration / candidate fitting / filtering / breakout tagging / duplicate grouping / best-from-group selection 这一整条链,在当前窗口里各自对应什么动作与什么数量级。

step stage what_happens_now current_window_evidence
1 pivot collection 先在当前 96 根 K 线窗口里找 support / resistance pivots。 support=23,resistance=22
2 pivot pair enumeration 只允许 pivot-pair 进入拟合;不是任意两根 bar 都能拿来画线。 support=253,resistance=231
3 candidate line fitting 对每个合法 pivot-pair 用两点拟合出一条直线(m / b)。 这是候选线生成层,还不是最终趋势线。
4 validity filtering 先过滤 slope / last-price 不合理的线,再检查有多少点足够贴近该线。 通过基础过滤后:support=253,resistance=231
5 valid trendline 只有命中点数达到 `num_points >= 3` 的线,才进入有效结果池。 有效结果:support=133,resistance=95
6 breakout tagging 对有效线再判断当前窗口里是否已经出现 breakout。 breakout tagged:support=130,resistance=93
7 duplicate grouping 把几乎重合的线聚成 group,避免页面被视觉上重复的线淹没。 duplicate groups:support=125,resistance=90
8 best-from-group selection 每组最终只保留 score 最好的代表线优先展示。 best-from-group:support=125,resistance=90

什么情况下能被当做趋势线,什么情况下不能

这个区块回答:一条线要满足哪些条件才会变成有效趋势线;又会因为什么原因被淘汰、被改标 breakout,或虽存在但不再单独展示。

question 能成为趋势线的条件 不能成为或不再单独展示的原因
什么线能进入候选池 start / end 必须来自 pivots。 若起点或终点不是 pivot,在当前配置下会直接跳过。
什么线能变成有效趋势线 除了两端外,还要有足够多的点贴近它,当前至少 `num_points >= 3`。 只靠两点定义出来的线解释力太弱,不进入最终有效结果。
贴近这条线是什么意思 价格点到线的误差要小于 `max_allowable_error ≈ 3.8836`。 误差太大就不算命中,因此不会增加 num_points。
什么线会在第一层就被淘汰 slope 不合法,或这条线在窗口最后一根 bar 的价格位置明显不合理。 这些线连“基础几何位置”都不成立,所以不会继续参与后续评分。
breakout 会改变什么 若价格越线幅度超过 `breakout_tolerance ≈ 5.1782`,这条有效线会被标记为 breakout。 它不是被删除,而是从“静态结构线”变成“已发生事件的结构线”。
为什么有些线存在却不单独展示 因为 duplicate grouping 会把几乎重合的线放进同一组。 若它不是该组 score 最好的那条,就仍然存在于结果表里,但不会优先画在教学视图上。

Filter waterfall:候选线是如何一层层被筛下来的

这个区块回答:从 pivot 到最终展示线,中间到底经过了多少层过滤。它是理解“为什么页面最后只剩少数代表线”的核心计数视角。

filter waterfall
stage support_count resistance_count why_it_matters
pivots 23 22 进入候选池的结构锚点数量
pivot_pairs_considered 253 231 在当前配置下真正被拿来拟合直线的 pivot 对数量
pass_basic_filters 253 231 通过 slope / last-price 合法性过滤后的候选对数量
valid_results_pre_group 133 95 通过最小命中点数与 pointset 去重之后留下的全部有效结果
breakout_tagged 130 93 这些有效结果里被标成 breakout 的数量
duplicate_groups 125 90 duplicate grouping 之后形成的组数
best_from_group 125 90 页面最终优先展示的代表线数量

Accepted vs rejected examples:抽象规则对应到真实样例

这个区块回答:上面的规则如果落到当前窗口里的真实线,分别长什么样。这里刻意同时给出:被保留的、被改标 breakout 的、虽然有效但被并组压掉的、以及根本没进有效结果池的样例。

example_role side current_status concrete_case why_this_case_matters
Accepted example resistance valid non-breakout representative line_id=R-[24,34,35] | score=127.69 | num_points=3 | pointset=[24, 34, 35] | group=1056 它同时满足有效趋势线条件,而且还是 duplicate group 里的最高分代表线,所以会直接出现在当前教学视图里。
Accepted example resistance breakout-tagged representative line_id=R-[41,59,60] | score=35305.89 | num_points=3 | pointset=[41, 59, 60] | breakout_index=67 | group=1074 它先是一条有效结构线,随后又被标成 breakout;因此它不会消失,而是会在 Step 4 里以事件线的身份出现。
Grouped-away example resistance valid but not shown as representative line_id=R-[53,75,76] | score=610.39 | num_points=3 | pointset=[53, 75, 76] | breakout_index=59 | group=1078 它其实已经是有效趋势线,但因为与另一条线几乎重合,被 duplicate grouping 压进同组;同组代表线 id=R-[53,73,75,76] score=798.88,所以页面默认优先展示代表线。
Rejected candidate support rejected at min-points filter pivot_pair=[0,5] | slope=-4205.36 | pointset=[0, 5] | num_points=2 这条线只命中了 2 个点,低于当前 `min_points_required=3`,所以它能被画出来,但还不够资格被当成有效趋势线。

Line lifecycle:一条线从 candidate 到代表线会经历什么

这个区块回答:一条线什么时候还只是 candidate,什么时候已经是有效结构线,什么时候被标成 breakout,什么时候只是留在结果里但不再默认展示,以及当前页面有没有“失效/过期线”的概念。

lifecycle_state current_window_evidence what_it_means_now how_it_shows_up_in_report
candidate pivot-pair support=253,resistance=231 只要起点/终点是当前窗口里允许的 pivots,这条线就先进入候选拟合层。此时它只是“可尝试拟合的一条线”,还不是有效结构。 默认不直接展示;只在 filter waterfall 里体现总量。
rejected before result pool support=120,resistance=136 若 slope / last-price 不合法,或 `num_points` 不够,或命中点集合和已有结果重复,就会在进入最终结果池前被淘汰。 默认不单独画图;当前只在 `accepted vs rejected examples` 里抽真实个案做解释。
valid non-breakout line support=3,resistance=2 通过基础过滤与最小命中点数后,且当前窗口里还没有被标成 breakout 的有效结构线。 若它同时是 best-from-group,就会进入 Step 2 / Step 3 教学视图与代表线表。
breakout-tagged line support=130,resistance=93 它先是一条有效结构线,随后在当前窗口里又被打上 breakout 标签;这改变的是“状态解释”,不是把它从结果池里删除。 会在 Step 4 与结果表里作为事件线继续保留。
grouped but not representative support=8,resistance=5 这条线本身仍然有效,只是它与别的线几乎重合,duplicate grouping 后没有拿到组内最高分。 保留在 CSV / 原始结果里,但默认不优先画进教学图。
best-from-group representative support=125,resistance=90 这是 duplicate group 里 score 最好的代表线,属于页面默认最该先看的那一小部分。 会优先出现在 Step 2 / Step 3 / Step 4 与 Best lines 表里。
expired / invalidated state? 当前页面:未单独建模 这页目前没有“线后来彻底失效、从结果里退休”的单独生命周期状态;当前实现一旦识别到 breakout,更接近把它保留成带事件标签的研究对象。 因此页面更像“窗口末端回头看的一张状态快照”,而不是 bar-by-bar 的存活/退场审计。

State diagram:line lifecycle 状态流转图

这个区块回答:上面的生命周期如果压缩成一张图,状态之间的流转顺序是什么;同时明确当前页面还没有单独建模 expired / retired state。

line lifecycle state diagram

`num_points` 应该怎么读

这个区块回答:`num_points` 不是交易次数,而是这条线被多少结构点支撑;它帮助判断这条线的解释力强弱。

question current_answer why_it_matters
`num_points` 怎么读 它表示有多少个价格点足够贴近这条线。 不是交易次数,而是结构支撑强度的粗略代理。
为什么 2 点不够 2 点几乎总能定义一条直线。 如果只用 2 点,页面会充满解释力很弱的偶然连线。
为什么还需要 `score` 命中点数量相同的线,和价格贴合误差可能不同。 `score` 让我们在同类线中优先看更贴近、点数更多的线。

`is_breakout` 应该怎么读

这个区块回答:support breakout / resistance breakout 到底分别是什么意思,它更接近 close-based 还是 high/low-based,以及它在当前页面里更像什么类型的标签。

question current_answer why_it_matters
support breakout 是什么 当 support 线在某个 bar 上方,且 `trend_price > Low + tolerance`。 当前更接近 low-based breach,而不是 close-based cross。
resistance breakout 是什么 当 resistance 线在某个 bar 下方,且 `trend_price < High - tolerance`。 当前更接近 high-based breach,而不是 close-based cross。
当前 tolerance ≈ 5.1782 源码用 `breakout_tolerance = avg_candle_range * 0.08`,避免太小的穿越也被标成 breakout。
事件发生时点 源码先记录 `breakout_index`,本地 bridge 会据此修正 `breakout_date` 到真实事件 bar。 这样报告里的 breakout 日期就不再误指向起始锚点日期。
实时可用性边界 它是基于当前窗口扫描结果打出来的研究标签。 可用于理解结构被突破了没有,但不能直接等同于已经审计过的实时交易信号。

Duplicate grouping:support 侧概览

这个区块回答:support 侧为什么会出现很多近似线,以及当前窗口下它们被压缩成了多少个 group。

metric value
support_all_lines 133.0000
support_duplicate_groups 125.0000
support_best_from_group_lines 125.0000
support_avg_group_size 1.0600
support_max_group_size 2.0000
support_dup_threshold_last_price 12.9455
support_dup_threshold_slope 3.2364

Duplicate grouping:resistance 侧概览

这个区块回答:resistance 侧为什么也会有大量相近线,以及 grouping 阈值当前大概落在什么数量级。

metric value
resistance_all_lines 95.0000
resistance_duplicate_groups 90.0000
resistance_best_from_group_lines 90.0000
resistance_avg_group_size 1.0600
resistance_max_group_size 2.0000
resistance_dup_threshold_last_price 12.9455
resistance_dup_threshold_slope 3.2364

Support duplicate groups(样例)

这个区块回答:support 侧 group 内通常有多少条近似线,哪一条会被选成 best-from-group。

group_id side group_size best_line_id best_score contains_breakout
2051 support 2 S-[25,49,54,64,73] 5422.271111 True
2086 support 2 S-[49,61,70] 1219.355561 True
2054 support 2 S-[25,49,51,64,71,77] 573.731215 True
2052 support 2 S-[49,54,64,71,77] 251.230048 True
2084 support 2 S-[48,54,64,71,77] 204.417931 True
2078 support 2 S-[46,49,61,67,79] 82.014429 True
2079 support 2 S-[46,48,50,52,86] 34.422409 True
2082 support 2 S-[49,60,92,94] 15.038261 True

Resistance duplicate groups(样例)

这个区块回答:resistance 侧 group 内的近似线如何聚类,以及 breakout / non-breakout 为什么不会混进同一组。

group_id side group_size best_line_id best_score contains_breakout
1038 resistance 2 R-[16,69,75] 3239.428949 True
1078 resistance 2 R-[53,73,75,76] 798.880263 True
1042 resistance 2 R-[20,21,35,67,76] 476.61005 True
1066 resistance 2 R-[28,46,73,75,76] 291.278369 True
1052 resistance 2 R-[35,67,76] 68.082019 True
1074 resistance 1 R-[41,59,60] 35305.885985 True
1036 resistance 1 R-[16,41,63] 35177.966123 True
1016 resistance 1 R-[5,12,83] 20911.947869 True

Duplicate grouping before/after:压缩前后到底差了什么

这个区块回答:duplicate grouping 不是抽象概念,而是把一批几乎重合的候选结果压缩成每组一条代表线。左侧子图展示 grouping 压缩前的重复线堆叠,右侧子图展示压缩后仅保留 best-from-group 的效果。

duplicate grouping before after

图下注释:颜色表示被选中的 duplicate groups;before 面板里同组内会看到多条非常接近的线,after 面板里只保留每组得分最高的代表线。这样更容易理解:页面默认不是忽略其它线,而是有意识地做了“视觉去重”。

如何理解 duplicate grouping 与 score

这个区块回答:为什么会有很多相似线、group 内 best line 是怎么选的、以及为什么页面默认只展示 best-from-group。

问题当前回答
为什么会有大量相近线?因为不同 pivot-pairs 可能拟合出 slope 与最后价格都非常接近的线;它们在视觉上几乎重合,但在源码里仍是不同候选结果。
group 是按什么聚的?pytrendline 用二维条件聚类:同时比较 price_at_last_dateslope 的差异;两者都足够接近时才会归为同一组。
breakout 线会和普通线混组吗?不会。源码要求 is_breakout 相同才允许进同一组,所以 breakout / non-breakout 会分开聚类。
best-from-group 怎么选?每个 duplicate group 里最终按 score 选出代表线,并标成 is_best_from_duplicate_group=True
为什么页面默认只展示 best-from-group?否则图上会堆满视觉上几乎重合的线,读者很难判断真正值得先看的结构;保留代表线能显著提升可读性。
读图时该怎么理解 rank_within_group它表示同组内部按 score 排名的位置;数字越靠前,说明它越接近该组最值得展示的代表线。

第三层:结果层

这一层只看当前窗口里真正保留下来的图、代表线、事件线与汇总结果。先有了前面的定义和计算,再回来看这些结果就不会误读成“所有线都是实时交易信号”。

窗口内数量概览

这个区块回答:当前窗口里总共识别出了多少 pivots、多少候选/代表线、多少 breakout 标签。

metric value
bars_in_window 96
support_pivots 23
resistance_pivots 22
support_lines_all 133
resistance_lines_all 95
support_lines_best 125
resistance_lines_best 90
support_breakouts 130
resistance_breakouts 93

Step 1 · 先看结构锚点:原始 K 线 + pivots

这个区块回答:趋势线依附的“结构点”到底在哪里。当前 pivots 来自原始 OHLC,而不是平滑后的曲线;每个三角标旁的小数字就是对应的 pivot index。

close and pivots

图下注释:先只看 K 线与 pivot 标记,不急着看趋势线。图上的小数字就是 pivot index,这样 Step 1 现在已经可以直接和 deep-dive / best lines 表里的 `pointset_indeces` 对照;读法上,先确认支撑/阻力锚点分布,再进入后面的支撑线与阻力线图。

Step 2 · 再看支撑线如何从锚点长出来

这个区块回答:support line 在局部到底是怎么被“贴”到多个结构点上的。这里继续只保留 grouped 之后的代表线,但不再把 breakout 线排除在外;若某条线后来被突破,前半段仍会画出来,突破后的后半段再改成虚线。

support lines

图下注释:蓝色实线 = 当前窗口里这条代表 support 线在 breakout 前的结构段;绿色虚线 = 同一条线在 breakout 后的延伸段;若某条线尚未 breakout,则仍按普通代表线显示。白边高亮点 = 这条线实际命中的 pivots(数字是 pivot index)。所以 Step 2 现在回答的是“代表线是怎么长出来的”,而不是“当前还剩多少 non-breakout 线”。

Step 3 · 再看阻力线如何从锚点长出来

这个区块回答:resistance line 在局部到底是怎么被“贴”到多个结构点上的。这里同样只保留 grouped 之后的代表线;若后来发生 breakout,就把突破后的后半段改为虚线而不是整条线直接隐藏。

resistance lines

图下注释:紫色实线 = breakout 前仍作为结构线的一段;红色虚线 = breakout 后的延伸段;白边高亮点 = 这条线命中的 pivots。这样你可以同时看到“线是怎么被定义出来的”和“它后来在哪里变成事件线”。

Step 4 · 最后只看事件线:breakout-only

这个区块回答:哪些线已经不只是“结构线”,而是被 pytrendline 标成了 breakout 事件线。这里改成 support / resistance 分面显示,减少不同侧事件线互相压在一起。

breakout only lines

图下注释:每个子图只保留少量 breakout 样例线。实线部分表示用于定义结构的拟合线段,虚线部分表示向 breakout bar 的延伸;带描边的事件圈和 `S/R` 标记,就是实际触发 breakout 的那根 K 线。这样你看到的是“这条线如何变成事件”,而不是一整屏互相重叠的无限延长线。

最终总览图:把锚点、代表线与事件线叠在一起

这个区块回答:如果把 pivots、代表线和 breakout 线放回同一张图,整体画面是什么样。

trendlines overlay

图下注释:这张总览图现在与 Step 2 / Step 3 保持一致——同一条 breakout 线会拆成“break 前实线 + break 后虚线”,并额外圈出 breakout bar。为了减少视觉噪声,这里不再铺满当前窗口的全部 pivots,而只高亮当前代表线实际命中的那些 support / resistance pivots;如果你要看全量 pivot universe,请回到 Step 1。

Selected line deep-dive:挑 1 条 support / 1 条 resistance 逐项拆开

这个区块回答:如果不只看抽象规则,而是直接挑两条真实代表线逐项拆开,它们到底命中了哪些 pivots、`m / b / num_points / score` 怎么读、为什么通过过滤、为什么是 best-from-group、以及 breakout 发生在哪根 bar。

Support selected line

aspect current_window_answer
为什么选这条 它不是全窗口绝对最高分线,而是“同组不止 1 条时”里最适合教学展开的代表线:既能讲结构点,也能具体解释 best-from-group 是怎么赢出来的。
line id / side S-[25,49,54,64,73] / support
命中的 pivots 25@03-12 02:50, 49@03-12 04:50, 54@03-12 05:15, 64@03-12 06:05, 73@03-12 06:50
m / b m=3.911784,b=69154.806966;也就是 `trend_price = m * bar_index + b`。
num_points 5(当前阈值是 >= 3)
为什么通过 slope filter 归一化 slope ≈ 253.1998,落在允许区间 [-179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0000, 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0000] 内。
为什么通过 last-price filter 线在窗口最后一根 bar 上的价格 ≈ 69526.43,落在允许区间 [46581.63, 104756.29] 内。
为什么是 best-from-group group=2051,共 2 条近似线;当前这条 `score=5422.27` 高于同组第二名 `S-[46,51,71,77]` 的 `score=49.57`,所以它是默认代表线。
breakout 事件 bar breakout_index=46(03-12 04:35 UTC) | Open=69449.27, High=69449.27, Low=69326.49, Close=69369.39 | Low 是当前侧 breakout 判定更直接参考的价格列。

同组对照:下面列的是和这条 support 线处在同一个 duplicate group 的候选结果,方便直接看出它为什么成为代表线。

id score num_points rank_within_group is_breakout pointset_indeces
S-[25,49,54,64,73] 5422.271111 5 1 True 25, 49, 54, 64, 73
S-[46,51,71,77] 49.567911 4 2 True 46, 51, 71, 77

Resistance selected line

aspect current_window_answer
为什么选这条 它不是全窗口绝对最高分线,而是“同组不止 1 条时”里最适合教学展开的代表线:既能讲结构点,也能具体解释 best-from-group 是怎么赢出来的。
line id / side R-[16,69,75] / resistance
命中的 pivots 16@03-12 02:05, 69@03-12 06:30, 75@03-12 07:00
m / b m=-6.312647,b=69979.346108;也就是 `trend_price = m * bar_index + b`。
num_points 3(当前阈值是 >= 3)
为什么通过 slope filter 归一化 slope ≈ -408.6016,落在允许区间 [-179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0000, 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0000] 内。
为什么通过 last-price filter 线在窗口最后一根 bar 上的价格 ≈ 69379.64,落在允许区间 [46581.63, 104756.29] 内。
为什么是 best-from-group group=1038,共 2 条近似线;当前这条 `score=3239.43` 高于同组第二名 `R-[16,69,75,77]` 的 `score=125.74`,所以它是默认代表线。
breakout 事件 bar breakout_index=76(03-12 07:05 UTC) | Open=69507.77, High=69507.77, Low=69461.32, Close=69461.32 | High 是当前侧 breakout 判定更直接参考的价格列。

同组对照:下面列的是和这条 resistance 线处在同一个 duplicate group 的候选结果,方便直接看出它为什么成为代表线。

id score num_points rank_within_group is_breakout pointset_indeces
R-[16,69,75] 3239.428949 3 1 True 16, 69, 75
R-[16,69,75,77] 125.742941 4 2 True 16, 69, 75, 77

Best support lines

这个区块回答:当前最值得先读的代表支撑线有哪些,它们各自的 `m / b / score / num_points` 是什么,以及它们命中了哪些结构点。

id is_breakout pointset_indeces pointset_dates breakout_index breakout_date num_points m b score starts_at_date ends_at_date
S-[19,46,56] True 19, 46, 56 03-12 02:20 | 03-12 04:35 | 03-12 05:25 21 2026-03-12T02:30:00 3 -8.466725 69715.961516 14706.65938 2026-03-12 02:20:00+00:00 2026-03-12 05:25:00+00:00
S-[25,48,65,74] True 25, 48, 65, 74 03-12 02:50 | 03-12 04:45 | 03-12 06:10 | 03-12 06:55 46 2026-03-12T04:35:00 4 4.27106 69145.825068 5829.01929 2026-03-12 02:50:00+00:00 2026-03-12 06:55:00+00:00
S-[25,49,54,64,73] True 25, 49, 54, 64, 73 03-12 02:50 | 03-12 04:50 | 03-12 05:15 | 03-12 06:05 | 03-12 06:50 46 2026-03-12T04:35:00 5 3.911784 69154.806966 5422.271111 2026-03-12 02:50:00+00:00 2026-03-12 06:50:00+00:00
S-[23,38,53,59,61,65] True 23, 38, 53, 59, 61, 65 03-12 02:40 | 03-12 03:55 | 03-12 05:10 | 03-12 05:40 | 03-12 05:50 | 03-12 06:10 25 2026-03-12T02:50:00 6 0.270765 69403.295847 5329.810075 2026-03-12 02:40:00+00:00 2026-03-12 06:10:00+00:00
S-[46,51,64,73] True 46, 51, 64, 73 03-12 04:35 | 03-12 05:00 | 03-12 06:05 | 03-12 06:50 55 2026-03-12T05:20:00 4 4.28125 69129.554687 5017.632378 2026-03-12 04:35:00+00:00 2026-03-12 06:50:00+00:00
S-[23,38,53,59,65,71] True 23, 38, 53, 59, 65, 71 03-12 02:40 | 03-12 03:55 | 03-12 05:10 | 03-12 05:40 | 03-12 06:10 | 03-12 06:40 25 2026-03-12T02:50:00 6 0.374837 69400.902181 4648.95911 2026-03-12 02:40:00+00:00 2026-03-12 06:40:00+00:00
S-[46,92,94] True 46, 92, 94 03-12 04:35 | 03-12 08:25 | 03-12 08:35 49 2026-03-12T04:50:00 3 10.185971 68857.9375 3388.615008 2026-03-12 04:35:00+00:00 2026-03-12 08:35:00+00:00
S-[9,48,55] True 9, 48, 55 03-12 01:30 | 03-12 04:45 | 03-12 05:20 19 2026-03-12T02:20:00 3 -12.162059 69934.614784 3325.186629 2026-03-12 01:30:00+00:00 2026-03-12 05:20:00+00:00
S-[63,71,73,76] True 63, 71, 73, 76 03-12 06:00 | 03-12 06:40 | 03-12 06:50 | 03-12 07:05 77 2026-03-12T07:10:00 4 6.433594 68970.730469 3216.271189 2026-03-12 06:00:00+00:00 2026-03-12 07:05:00+00:00
S-[63,92,93,95] True 63, 92, 93, 95 03-12 06:00 | 03-12 08:25 | 03-12 08:30 | 03-12 08:40 71 2026-03-12T06:40:00 4 14.448276 68465.805496 3033.445819 2026-03-12 06:00:00+00:00 2026-03-12 08:40:00+00:00
S-[46,48,50,52] True 46, 48, 50, 52 03-12 04:35 | 03-12 04:45 | 03-12 04:55 | 03-12 05:05 49 2026-03-12T04:50:00 4 12.171875 68766.585938 2766.13067 2026-03-12 04:35:00+00:00 2026-03-12 05:05:00+00:00
S-[29,49,51] True 29, 49, 51 03-12 03:10 | 03-12 04:50 | 03-12 05:00 36 2026-03-12T03:45:00 3 1.285547 69283.492578 2622.314288 2026-03-12 03:10:00+00:00 2026-03-12 05:00:00+00:00

Best resistance lines

这个区块回答:当前最值得先读的代表阻力线有哪些,它们和支撑线的差别是什么,以及它们命中了哪些结构点。

id is_breakout pointset_indeces pointset_dates breakout_index breakout_date num_points m b score starts_at_date ends_at_date
R-[41,59,60] True 41, 59, 60 03-12 04:10 | 03-12 05:40 | 03-12 05:45 67 2026-03-12T06:20:00 3 -6.679687 69926.757812 35305.885985 2026-03-12 04:10:00+00:00 2026-03-12 05:45:00+00:00
R-[16,41,63] True 16, 41, 63 03-12 02:05 | 03-12 04:10 | 03-12 06:00 59 2026-03-12T05:40:00 3 -9.018125 70022.63375 35177.966123 2026-03-12 02:05:00+00:00 2026-03-12 06:00:00+00:00
R-[5,12,83] True 5, 12, 83 03-12 01:10 | 03-12 01:45 | 03-12 07:40 81 2026-03-12T07:30:00 3 -3.926339 70035.787946 20911.947869 2026-03-12 01:10:00+00:00 2026-03-12 07:40:00+00:00
R-[1,12,16,17,31] True 1, 12, 16, 17, 31 03-12 00:50 | 03-12 01:45 | 03-12 02:05 | 03-12 02:10 | 03-12 03:20 32 2026-03-12T03:25:00 5 -27.509233 70318.78267 17609.864328 2026-03-12 00:50:00+00:00 2026-03-12 03:20:00+00:00
R-[20,81,89] True 20, 81, 89 03-12 02:25 | 03-12 07:30 | 03-12 08:10 84 2026-03-12T07:45:00 3 1.592853 69610.510118 15585.690465 2026-03-12 02:25:00+00:00 2026-03-12 08:10:00+00:00
R-[16,35,39,46] True 16, 35, 39, 46 03-12 02:05 | 03-12 03:40 | 03-12 04:00 | 03-12 04:35 40 2026-03-12T04:05:00 4 -14.282484 70106.863487 7078.11048 2026-03-12 02:05:00+00:00 2026-03-12 04:35:00+00:00
R-[12,16,17,31] True 12, 16, 17, 31 03-12 01:45 | 03-12 02:05 | 03-12 02:10 | 03-12 03:20 32 2026-03-12T03:25:00 4 -27.582031 70319.65625 5857.688478 2026-03-12 01:45:00+00:00 2026-03-12 03:20:00+00:00
R-[16,21,25,27] True 16, 21, 25, 27 03-12 02:05 | 03-12 02:30 | 03-12 02:50 | 03-12 03:00 17 2026-03-12T02:10:00 4 -48.29375 70651.04375 4603.659863 2026-03-12 02:05:00+00:00 2026-03-12 03:00:00+00:00
R-[21,81,89] True 21, 81, 89 03-12 02:30 | 03-12 07:30 | 03-12 08:10 84 2026-03-12T07:45:00 3 1.710937 69600.945312 4045.466105 2026-03-12 02:30:00+00:00 2026-03-12 08:10:00+00:00
R-[69,85,88] True 69, 85, 88 03-12 06:30 | 03-12 07:50 | 03-12 08:05 90 2026-03-12T08:15:00 3 19.008789 68232.166992 3988.341423 2026-03-12 06:30:00+00:00 2026-03-12 08:05:00+00:00
R-[12,24,25] True 12, 24, 25 03-12 01:45 | 03-12 02:45 | 03-12 02:50 16 2026-03-12T02:05:00 3 -41.959635 70492.1875 3877.185485 2026-03-12 01:45:00+00:00 2026-03-12 02:50:00+00:00
R-[28,46,53,66,72,74] True 28, 46, 53, 66, 72, 74 03-12 03:05 | 03-12 04:35 | 03-12 05:10 | 03-12 06:15 | 03-12 06:45 | 03-12 06:55 31 2026-03-12T03:20:00 6 1.74375 69365.714062 3447.057008 2026-03-12 03:05:00+00:00 2026-03-12 06:55:00+00:00

第四层:边界层

最后单独收束:哪些字段带事后视角、哪些结论现在能信、哪些不能直接跳到交易层,以及这页与后续 event foundation / channel 研究是什么关系。

时间语义 / 生命周期边界:现在看到的线,到底是“当时知道的”还是“回头看知道的”?

这个区块回答:这页里的 bars、pivots、trendlines、breakout 标签、duplicate grouping 各自是在什么时点才算已知;也明确当前页面更接近窗口末端回看快照,而不是 bar-by-bar 在线状态机。

object_or_field when_it_can_be_treated_as_known why_this_has_hindsight_or_delay how_to_read_it_in_this_report
当前页面整体视角 窗口末端重新扫描后的回看快照 当前整页是对最近 96 根 bar 一次性重算后的结果汇总,而不是当时逐根 bar 在线更新的状态日志。 最适合做结构解释 / 方法对照,不应直接当成实时信号审计结果。
原始 OHLC bars 每根 bar 收盘后即可视为已知 历史 K 线本身没有未来函数;一旦 bar 收完,OHLC 就能被当作当时已知的数据。 所以图上的价格历史可以当基础事实,但后面的结构标签不一定能同一时点同步知道。
pivot 身份 需要右侧若干根 bar 之后才能确认 局部高/低点要和后面的 bars 比较,因此 pivot 的时间戳落在过去,但“它是 pivot”这件事带事后视角。 可把 pivot 当成研究锚点;若要实时使用,必须单独审计确认时点。
candidate / valid line 晚于 pivot 确认之后 候选线要先完成 pivot-pair 枚举,再经过基础过滤与 `num_points` 检查;当前窗口最终留下 support=133、resistance=95 条有效结果。 因此页面里的线更接近“窗口末端重新回头看得到的结构对象”,不是当时逐 bar 持有的线集合。
`num_points` / `pointset_indeces` 候选线扫描完后才稳定 这些字段依赖整条线在窗口内命中了哪些点;它们更像结构解释字段,而不是实时逐根累加日志。 适合解释“这条线为什么成立”,不应直接解读为交易时当场已知的完整证据。
`is_breakout` / `breakout_index` / `breakout_date` 事件 bar 出现后,且在窗口扫描时统一打标 当前窗口里被标成 breakout 的有效结果:support=130、resistance=93。事件 bar 可以定位到历史某一根,但标签本身是整窗扫描后给出的研究标记。 可用来解释“哪根 bar 触发了 breach”,但还不是经过 bar-by-bar 审计的正式实时触发器。
`duplicate_group_id` / `is_best_from_duplicate_group` 窗口末端的展示压缩层 这些字段是为了把近似重合线压缩成更可读的代表线;当前 duplicate groups:support=125、resistance=90。 它们更像报告页面的阅读辅助状态,而不是交易执行时序状态。
expired / retired state 当前未单独建模 当前页面会把 breakout 后的线保留为研究事件线,而不是继续追踪它何时完全失效、退场或被替换。 如果后面要做正式 signal engine,需要另建 bar-by-bar state machine 来定义进入、确认、失效与退场。

字段解释

这个区块回答:表里的字段具体是什么意思,避免把 `score`、`is_breakout` 等字段误读成交易结论。

field operational_definition
m 趋势线斜率;正值表示线向上倾斜,负值表示线向下倾斜。
b 截距;与 `m` 一起决定整条线在窗口内的价格位置。
num_points 这条线命中的结构点数量;通常越多,解释性越强。
score pytrendline 对该线的排序分数;用于同类候选线比较,不应被误读为未来收益分数。
starts_at_date / ends_at_date 当前结果里该线覆盖的起止时间;用于说明线段在图上的定义区间。
is_best_from_duplicate_group 该线是否是相似线分组里的代表线。
is_breakout 该线在当前窗口里被标记为已发生 breakout;更像研究标签,不直接等于可实时交易信号。
pointset_indeces / pointset_dates 这条线命中的结构点索引与对应时间;用于把图上的白心圆点和表格里的锚点信息直接对上。
breakout_index / breakout_date breakout 事件第一次被识别到的 bar 索引与日期;本地 bridge 会把日期对齐到真实 breakout bar。

bar-by-bar 可用性 / research-only 边界

这个区块回答:这页最适合支持什么判断,以及目前还不应该被过度解读成什么。

question answer
当前适合什么 报告阅读、结构 inspection、外部方法对照、后续 parallel channel 研究输入。
当前不适合什么 直接当正式 signal engine、全量长样本高频扫描、未经 bar-by-bar 审计就上实盘。
为什么不是正式信号 因为 pivot / line / breakout 的最终结果更偏研究对象;是否能 bar-by-bar 使用,还需额外做因果审计。
和 parallel channel 的关系 它已经覆盖了 pivot → line → breakout 这半条链,但还没解决“双边平行约束 + 通道宽度 + 多周期共振”完整问题。

Baseline v1 status:这页当前处在什么阶段

这个区块回答:`pytrendline_research` 现在是一个什么性质的产物——它已经完成了哪些 explainability 任务,又明确还没有承担哪些策略层职责。

status_question current_answer why_it_matters
当前阶段 explainability baseline v1(可冻结的研究解释页) 当前目标是把结构识别、候选线生成、duplicate grouping、breakout 标签与时间语义讲清楚,而不是直接给出可交易结论。
它最适合做什么 结构解释 / 外部方法映射 / 后续 foundation report 的前置教材 后续 `trendline_event_foundation_report` 可以直接复用这里的 line lifecycle、candidate explosion、grouping 压缩与 breakout 语义。
它当前不是什么 正式交易信号 / bar-by-bar 审计引擎 / 最终收益证明 如果读者想知道 event 是否有 alpha / feature 价值,需要进入下一阶段的 event foundation,而不是直接从这页跳到策略结论。

当前我应该相信什么 / 不该过度解读什么

这个区块回答:看完前面所有定义、图和表之后,这页最适合支撑哪些判断,以及哪些结论还不能直接从这里跳到交易层。

judgement current_take
当前我应该相信什么 这页已经能可靠说明:pytrendline 怎样从 pivots 走到 candidate lines、怎样过滤、怎样 grouping、怎样把部分结构线标成 breakout 事件线。
当前不该过度解读什么 不要把 `is_breakout=True` 直接读成“可追的突破信号”,也不要把高 `score` 直接读成“更高收益线”;这页解释的是结构对象,不是经过事件验证后的交易规则。
当前最合理的使用方式 把它当成 trendline / support-resistance 结构语义的基线页:先确认定义清楚,再去审计 slope、confirmation、feature value。

下一步建议:从 explainability baseline 到 event foundation

这个区块回答:既然这页已经把结构定义讲清楚,后面最自然的推进顺序应该是什么;也明确不建议再回到“先追求策略净值”的旧路径。

priority next_step why_now
1 进入 `trendline_event_foundation_report` 把当前页面里的结构线对象转成 event taxonomy、confirmation ladder、slope buckets 与 go/no-go 审计。
2 先做事件研究,再做策略 优先比较 breakout vs rebound、raw vs confirmed、不同 slope bucket 的差异,而不是马上追求完整策略收益最大化。
3 parallel channel 暂时保持候选分支 只有当 trendline event foundation 给出明确正面证据、且 channel 定义足够清晰时,再把它升回高优先级。

从 pytrendline 到 parallel channel 的映射

这个区块回答:当前这页对后续 parallel channel 研究到底已经提供了哪些可复用定义,还缺哪一半没有解决。

问题当前回答
它已经解决了什么?已经把 pivot → candidate lines → best lines → breakout labels 这条链跑通了。
它还没解决什么?还没有“双边平行约束 / 通道宽度定义 / mid-line 语义 / 多周期共振 / rebound 规则”的完整定义。
对后续最有价值的是什么?给我们一个可解释的外部基线:先把 line 是怎么来的讲清楚,再决定哪些定义要迁移进平行通道研究。

Artifacts

这个区块回答:如果你要回到原始产物进一步核对,这一页背后有哪些 CSV / PNG / JSON 文件可供复查。