第 9 章 · 01 AbuTLine 技术线与趋势判断


文档摘要

第 9 章 · 01 AbuTLine 技术线与趋势判断 本节摘要:本节解剖 ABU 技术分析的"分析中枢"—— 技术线对象( 约 655 行)。AbuTLine 继承 ,初始化后即 冻结属性,构造时自动算出 mean/std/high/low/close 五个核心量( , )。它的 类方法是一站式可视化入口,内部依次调用最优拟合、路程位移比、回归通道、骨架通道、比例分割、黄金分割、阻力支撑七大分析。 / 用一次拟合角度判定趋势(被 买入因子调用), 判断最少几次拟合能代表走势(被大盘震荡过滤装饰器 调用)。 用 装饰,惰性计算当前位置在 high-low 区间的相对位置。

第 9 章 · 01 AbuTLine 技术线与趋势判断

本节摘要:本节解剖 ABU 技术分析的"分析中枢"——AbuTLine 技术线对象(ABuTLine.py 约 655 行)。AbuTLine 继承 FreezeAttrMixin,初始化后即 _freeze() 冻结属性,构造时自动算出 mean/std/high/low/close 五个核心量(high = mean + std,low = mean - std)。它的 show_kl_pd 类方法是一站式可视化入口,内部依次调用最优拟合、路程位移比、回归通道、骨架通道、比例分割、黄金分割、阻力支撑七大分析。is_up_trend/is_down_trend 用一次拟合角度判定趋势(被 AbuUpDownTrend 买入因子调用),show_least_valid_poly 判断最少几次拟合能代表走势(被大盘震荡过滤装饰器 AbuLeastPolyWrap 调用)。scoreLazyFunc 装饰,惰性计算当前位置在 high-low 区间的相对位置。本节还会拆开 ABuTLExecute(纯计算函数库:shift_distance 路程位移比、regress_trend_channel 回归通道、support_resistance_pos+select_k_support_resistance 阻力支撑聚类、find_golden_point 黄金分割)、ABuTLGolden.calc_golden(返回 17 字段 namedtuple,被 AbuUpDownGolden 调用)、ABuTLJump.calc_jump 跳空缺口、ABuTLWave.calc_wave_std 波动量化、ABuTLSimilar 相似度匹配。读完本节,你将看懂 ABU 全部技术分析的算法骨架。

内容来源:原项目源码 abupy/TLineBu/ABuTLine.py(655 行)+ ABuTLExecute.py(692 行)+ ABuTLGolden.py/ABuTLJump.py/ABuTLWave.py/ABuTLAtr.py/ABuTLVwap.py/ABuTLSimilar.py

⚠️ 注意:本节是第 9 章的开篇。重点理解"为什么 AbuTLine 要冻结属性"——技术线对象一旦构造完成,它的 mean/std/high/low 就不该再变动(否则后续所有分析都基于漂移的基准),FreezeAttrMixin._freeze() 用元类级别的属性锁强制保证这一点。另外 scoreLazyFunc 装饰,意味着只有第一次访问时才计算,后续访问直接取缓存——这是 ABU 处理"昂贵但只算一次"的标准范式。

学习目标

阅读完本节,你应当能够:

  1. 解释 AbuTLine 的构造流程:fillna(bfill) → arr_to_numpy → 自动算 mean/std/high/low/close → _freeze() 冻结,以及为什么 high/low 用 mean±std 而不是 max/min。
  2. 说清 show_kl_pd 类方法的一站式流程(7 个分析子方法依次调用),以及它返回 AbuTLine 对象的设计意图。
  3. 逐行读懂 is_up_trend/is_down_trend:valid_poly(poly=1) 判断能否用一次拟合描述 → calc_regress_deg 算角度 → 与阈值(默认 ±5)比较。
  4. 解释 show_least_valid_poly 的用途(最少几次拟合能代表走势),以及它为什么被 AbuLeastPolyWrap 装饰器用于"大盘震荡大就封锁交易"。
  5. 理解 score 的 LazyFunc 装饰((close-low)/(high-low),high==low 退化为 0.8/0.2)。
  6. 逐行读懂 support_resistance_pos + select_k_support_resistance:Chebyshev 多项式拟合放大 3 倍 → bfgs 找极值点种子 → set 去重 → KMeans 从 1 到 N 聚类 → 方差阈值手肘法选最佳 k。
  7. 说清 calc_golden 返回的 17 字段 namedtuple(above618/below618/above382/below382/above950/.../below200),以及视觉黄金分割(find_golden_point)与统计黄金分割(find_golden_point_ex)的区别。

一、AbuTLine 对象:冻结属性 + 自动统计量

AbuTLine(ABuTLine.py:90-120)是技术线的统一封装。构造函数:

90 class AbuTLine(FreezeAttrMixin): 91 """技术线封装执行对外操作的对象类""" 93 def __init__(self, line, line_name, **kwargs): 104 # 把序列的nan进行填充,实际上应该是外面根据数据逻辑把nan进行填充好了再传递进来,这里只能都使用bfill填了 105 line = pd.Series(line).fillna(method='bfill') 106 self.tl = arr_to_numpy(line) 107 self.mean = kwargs.pop('mean', self.tl.mean()) 108 self.std = kwargs.pop('std', self.tl.std()) 109 self.high = kwargs.pop('high', self.mean + self.std) 110 self.low = kwargs.pop('low', self.mean - self.std) 111 self.close = kwargs.pop('close', self.tl[-1]) 113 self.x = np.arange(0, self.tl.shape[0]) 114 self.line_name = line_name 116 for k, v in kwargs: 117 # 需要设置什么都通过kwargs设置进来,不然_freeze后无法设置 118 setattr(self, k, v) 119 # 需要进行定稿,初始化好就不能动 120 self._freeze()

构造的五步:

  1. fillna(bfill):用后向填充补 NaN。源码注释坦承"应该是外面根据数据逻辑填好再传进来",这里只是兜底。
  2. arr_to_numpy:统一转成 numpy 数组(self.tl),后续所有计算都基于 numpy。
  3. 自动统计量:mean/std 从序列算,high=mean+std、low=mean-std(注意不是 max/min!用 ±1 个标准差作为高低边界,过滤极端值),close 取最后一个元素。
  4. kwargs 透传:外部可覆盖任何统计量(如 ABuTLVwap.calc_vwap 传入 mean=vwap),也可塞入额外属性。
  5. _freeze():冻结。冻结后任何 self.xxx = yyy 都会报错——技术线对象是不可变的。

x = np.arange(0, len(tl)) 是时间轴索引,后续拟合、绘制都要用。

💡 解剖要点:high/low 用 mean±std 而不是 max/min,是 ABU 的关键设计。max/min 容易被单根异常 K 线拉飞(比如一天涨停 +10%),而 mean±std 描述的是"统计意义上的高低边界",更稳健。score 函数(下面讲)正是基于这个 high/low 算相对位置,如果用 max/min,score 会被极值绑架。

二、show_kl_pd:一站式可视化(7 大分析)

show_kl_pd(ABuTLine.py:122-157)是技术分析的入口,传入 KL 数据和列名(默认 close),自动构造 AbuTLine 并依次跑 7 个分析:

122 @classmethod 123 def show_kl_pd(cls, kl_pd, key='close', show=True, **kwargs): 134 line = cls(kl_pd[key], key) 135 if show: 136 # 可以通过kwargs设置show的参数,先pop出来 137 zoom = kwargs.pop('zoom', False) 138 step_x = kwargs.pop('step_x', 1.0) 139 how = kwargs.pop('how', EShiftDistanceHow.shift_distance_close) 140 only_last = kwargs.pop('only_last', False) 142 line.show() 143 # 可视化技术线最优拟合次数 144 line.show_best_poly(zoom=zoom) 145 # 可视化技术线'路程位移比' 146 line.show_shift_distance(step_x=step_x, how=how) 147 # 可视化技术线拟合曲线及上下拟合通道曲线 148 line.show_regress_trend_channel(step_x=step_x) 149 # 可视化可视化技术线骨架通道 150 line.show_skeleton_channel(step_x=step_x) 151 # 可视化技术线比例分割的区域 152 line.show_percents() 153 # 可视化技术线黄金分割 154 line.show_golden() 155 # 对技术线阻力位和支撑位进行绘制, 以及所有中间过程 156 line.show_support_resistance_trend(only_last=only_last) 157 return line

七个分析子方法:

方法 算法核心 用途
show 画 tl + high/mean/low 三条横线 基本信息一览
show_best_poly search_best_poly 找 1-100 次最优拟合 走势用几次多项式描述最合适
show_shift_distance shift_distance 路程位移比 量化"走了多远 vs 位移多少"
show_regress_trend_channel regress_trend_channel 回归通道 上中下三轨拟合通道
show_skeleton_channel skeleton_how 骨架点位 min/max/mean 三轨骨架
show_percents + show_golden find_percent_point/find_golden_point 比例分割 + 黄金分割
show_support_resistance_trend support_resistance_pos+select_k+predict 阻力支撑位聚类与预测

返回 line(AbuTLine 对象),调用方可继续做后续分析(如取 line.score)。这种"构造 + 可视化 + 返回对象"的一站式设计,是 ABU 对外 API 的统一风格。

三、趋势判定:is_up_trend / is_down_trend / show_least_valid_poly

3.1 is_up_trend 与 is_down_trend(ABuTLine.py:221-254)

221 def is_up_trend(self, up_deg_threshold=5, show=True): 231 valid = ABuRegUtil.valid_poly(self.tl, poly=1, show=show) 232 if valid: 233 deg = ABuRegUtil.calc_regress_deg(self.tl, show=show) 234 if deg >= up_deg_threshold: 235 return True 236 return False

三步判定:

  1. valid_poly(poly=1):先用一次多项式拟合,判断"这条走势能不能用直线描述"(R² 达标才算 valid)。如果连直线都拟合不好(震荡太厉害),直接判 False。
  2. calc_regress_deg:算一次拟合的角度(度数)。
  3. 与阈值比较:角度 ≥ 5° 判上升,≤ -5° 判下降。

is_down_trend 完全对称,阈值默认 -5。这两个方法被 AbuUpDownTrend(ABuFactorBuyTrend.py:19)调用——这个买入因子在 fit_day 里先判断大盘是否处于上升走势,是才考虑买入。这就是"顺势交易"的代码落地。

3.2 show_least_valid_poly 与 AbuLeastPolyWrap(ABuTLine.py:268-278)

268 def show_least_valid_poly(self, zoom=False, show=True): 275 least = ABuRegUtil.least_valid_poly(self.tl, zoom=zoom, show=show) 276 if show: 277 log_func('least poly = {}, zoom={}'.format(least, zoom)) 278 return least

least_valid_poly 返回"最少需要几次多项式拟合才能代表走势"。如果 least=1,说明走势很平滑(一条直线就能描述);如果 least=10,说明走势锯齿严重(需要 10 次多项式才能拟合)。

这个方法被 AbuLeastPolyWrap(ABuBuyFactorWrap.py:15)用作装饰器:对大盘月线调用 show_least_valid_poly,如果 least 超过阈值,说明大盘震荡太大,封锁买入因子(不让它发信号)。源码注释:"示例做为买入因子策略装饰器封装 show_least_valid_poly 对大盘震荡大的情况下封锁交易"。这是 ABU 用"大盘走势复杂度"作为系统性风控的精妙设计。

四、score:LazyFunc 惰性打分

score(ABuTLine.py:159-175)是技术线的"当前位置打分":

159 @LazyFunc 160 def score(self): 171 if self.high == self.low: 172 score = 0.8 if self.close > self.low else 0.2 173 else: 174 score = (self.close - self.low) / (self.high - self.low) 175 return score

@LazyFunc 是 ABU 的惰性装饰器(ABuLazyUtil.py)——把方法变成 property,第一次访问时计算并缓存,后续访问直接取缓存。score 公式 (close - low) / (high - low) 给出当前收盘价在 high-low 区间的相对位置:

  • score=0:收盘价在 low(底部)。
  • score=1:收盘价在 high(顶部)。
  • score=0.5:收盘价在中位。

退化处理:high == low(序列无波动)时,close > low 给 0.8,否则给 0.2。这避免除零,也符合直觉(无波动时倾向于给中性偏高/偏低)。

score 被广泛使用——卖出因子的"高位卖出"、买入因子的"低位买入",很多都基于这个打分。LazyFunc 保证它只算一次,后续访问零开销。

五、ABuTLExecute:纯计算函数库

ABuTLExecute(692 行)是技术线的"算法厨房",AbuTLine 的可视化方法都委托给它。重点拆四个核心函数。

5.1 shift_distance:路程位移比(ABuTLExecute.py:38-137)

"位移路程比"是 ABU 自创的指标,衡量"价格走了多远 vs 实际位移多少"。一个直角三角形:底边是时间跨度(h_distance),垂直边是价格变化(v_distance),斜边是路程(distance)。位移(shift)是每日价格变动绝对值的累加。

66 h_distance = slice_end - slice_start # 时间跨度 68 v_distance = abs(shift_y - start_y) # 价格变化 70 distance = math.sqrt(h_distance ** 2 + v_distance ** 2) # 斜边(路程) 73 diff_slice = pd.Series(slice_arr).diff().fillna(value=0) 91 shift = np.abs(diff_slice).sum() # 位移(每日变动绝对值累加) 93 sd = shift / distance # 位移路程比

sd 越大,说明"走了很多路但位移很小"(震荡行情);sd 越小,说明"位移和路程接近"(单边趋势行情)。shift_distance_how(L263-290)提供三种确定路程终点点位的方法:close(最后一个)、maxmin(趋势方向取极值)、sum_maxmin(绝对距离靠近哪个极值取哪个)。

5.2 regress_trend_channel:回归通道(ABuTLExecute.py:293-319)

300 reg_model, y_fit = ABuRegUtil.regress_y(arr, zoom=False) 307 min_ind = (arr.T - y_fit).argmin() # 原始序列与拟合差值最小的点(最负) 309 below = x[min_ind] * b + a - arr[min_ind] # below 是差值 311 y_below = x * b + a - below # 拟合线下移 below 314 max_ind = (arr.T - y_fit).argmax() 316 above = x[max_ind] * b + a - arr[max_ind] 318 y_above = x * b + a - above # 拟合线上移 above(above 是负数)

先用一次回归拟合(arr → y_fit),再找原始序列中"离拟合线最远"的两个点(一个在最下方,一个在最上方),把拟合线平移到这两个点,形成上中下三轨通道。这是经典的线性回归通道画法。

5.3 support_resistance_pos + select_k_support_resistance:阻力支撑聚类

这是全模块最复杂的算法。support_resistance_pos(ABuTLExecute.py:341-387):

365 best_poly = ABuRegUtil.search_best_poly(support_resistance_y, poly_min=7, zoom=False, show=False) 367 best_poly *= int(math.ceil(len(x) / 120)) # 按序列长度倍乘 371 p = np.polynomial.Chebyshev.fit(x, support_resistance_y, best_poly) # Chebyshev 多项式 374 support_resistance = set() # set 去重 377 for index in xrange(0, len(support_resistance_y), 1): 379 local_min_pos = int(bfgs_min_pos(index, len(support_resistance_y), p)) 384 support_resistance.add(local_min_pos) # BFGS 找极值点种子

思路:

  1. 找最优拟合次数(L365-367):search_best_poly 从 7 次开始找,再按序列长度倍乘(120 天 ≈ 1 倍)。
  2. Chebyshev 多项式拟合(L371):用切比雪夫多项式(比普通多项式数值更稳定),次数是 best_poly。注意 AbuTLine 调用时传入的序列是 demean 后的(去均值),阻力位序列还要 ×-1(取反,这样找最小值就是找原始序列的最大值)。
  3. BFGS 找极值点(L377-384):对每个 index,用 scipy 的 fmin_bfgs 在 Chebyshev 模型上找局部最小值位置。用 set 去重(很多 index 会收敛到同一个极值点)。

得到的种子点位(阻力位或支撑位的 x 坐标)再喂给 select_k_support_resistance(ABuTLExecute.py:390-458)做 KMeans 聚类:

402 est_arr = [KMeans(n_clusters=k).fit(support_resistance) for k in k_rng] 403 sum_squares = [e.inertia_ for e in est_arr] # 每个 k 的方差和 413 diff_squares = [squares / sum_squares[0] for squares in sum_squares] # 归一化到 0-1 416 thresh_pd = diff_squares_pd[diff_squares_pd < thresh] # thresh 默认 0.06 429 select_k = k_rng[thresh_pd.index[0]] # 第一个低于阈值的 k

经典的手肘法:从 k=1 聚到 k=N,每个 k 算方差和(inertia_),归一化后找第一个低于阈值(0.06)的 k——这个 k 之后再加类别,方差下降就不明显了,所以是最佳聚类数。返回的 KMeans 对象的簇心就是阻力/支撑位的价格。

5.4 find_golden_point 与 find_golden_point_ex(ABuTLExecute.py:650-691)

671 def find_golden_point(x, y, show=False): 674 cs_max = y.max() 675 cs_min = y.min() 677 sp382 = (cs_max - cs_min) * 0.382 + cs_min # 视觉黄金分割 678 sp618 = (cs_max - cs_min) * 0.618 + cs_min 680 sp50 = (cs_max - cs_min) * 0.5 + cs_min 683 return sp382, sp50, sp618

两种黄金分割:

  • find_golden_point(视觉):用 max/min 算,(max-min)*0.382 + min,经典画法。
  • find_golden_point_ex(统计):用 stats.scoreatpercentile 算 38.2/50/61.8 百分位,对极值更鲁棒。

六、calc_golden:17 字段 namedtuple(ABuTLGolden.py)

calc_golden(ABuTLGolden.py:21-98)把视觉与统计黄金分割 + 多个比例分割组合成一个完整的"分割报告":

36 gd_382, gd_500, gd_618 = ABuTLExecute.find_golden_point(kl_pd.index, kl_close) # 视觉 38 gex_382, gex_500, gex_618 = ABuTLExecute.find_golden_point_ex(kl_pd.index, kl_close) # 统计 41 below618, above618 = ABuTLExecute.below_above_gen(gd_618, gex_618) # 618 带上下底 42 below382, above382 = ABuTLExecute.below_above_gen(gd_382, gex_382) # 382 带上下底 45 percents = [0.20, 0.25, 0.30, 0.70, 0.80, 0.90, 0.95] 46 pts_dict = ABuTLExecute.find_percent_point(percents, kl_close) 49 below200, _ = ABuTLExecute.below_above_gen(*pts_dict[0.20]) # 底部只取最低 57 _, above950 = ABuTLExecute.below_above_gen(*pts_dict[0.95]) # 顶部只取最高

关键设计:382 和 618 是"带"(视觉与统计两个值,取小为 below,取大为 above,形成区间);其余比例(0.20/0.25/0.30 底部,0.70/0.80/0.90/0.95 顶部)只取一个值——底部取较低、顶部取较高,符合"底部只要最低、顶部只要最高"的实战直觉。

返回 17 字段 namedtuple(L93-98):g382/gex382/g500/gex500/g618/gex618/above618/below618/above382/below382/above950/above900/above800/above700/below300/below250/below200。这个 namedtuple 被 AbuUpDownGolden(ABuFactorBuyTrend.py:57)使用——它在黄金分割位附近寻找买入机会。

七、跳空/波动/ATR/VWAP/相似度

7.1 calc_jump 跳空缺口(ABuTLJump.py:30-188)

跳空缺口的判定有三层过滤:

44 change_ratio_min = kl_pd['abs_pct_change'].mean() # 日振幅均值(第一层阈值) 48 change_mean = pd_resample(kl_pd.abs_pct_change, '21D', how='mean') # 月振幅(第三层阈值来源) 64 volume_mean = pd_resample(kl_pd.volume, '21D', how='mean') # 月成交量均值(第二层阈值) 144 if today.abs_pct_change <= change_ratio_min: # 第一层:振幅要超日均值 149 if today.volume <= volume_mean.loc[date]: # 第二层:成交量要超月均 161 jump_diff = today.pre_close * jump_threshold / 100 * jump_diff_factor # 跳空距离阈值 163 if today.p_change > 0 and (today.low - today.pre_close) > jump_diff: # 第三层:向上跳空 171 today['jump_power'] = (today.low - today.pre_close) / jump_diff # 跳空强度

注意向上跳空用 today.low(最低价)判断,向下跳空用 today.high(最高价)——这是为了保证跳空是"整根 K 线都在缺口那边",而不是影线穿透。

7.2 calc_wave_std 波动量化(ABuTLWave.py:37-63)

48 pre_close = kl_pd['close'].shift(1) 50 change = np.where(pre_close == 0, 0, np.log(kl_pd['close'] / pre_close)) # 对数收益 52 roll_std = pd_ewm_std(change, span=xd, min_periods=1, adjust=True) * np.sqrt(xd) # 加权移动 std 59 line = AbuTLine(roll_std, 'wave std') # 用 AbuTLine 封装

用对数收益的加权移动标准差量化波动,最后用 AbuTLine 封装(自动算 mean/std/high/low,后续可用 score 打分)。calc_wave_abscalc_wave_weight_abs 是变体(用绝对振幅 + demean + 时间加权)。

7.3 calc_atr_std 与 calc_vwap

  • ABuTLAtr.calc_atr_std:对 ATR 序列取对数收益再算移动 std(波动率的波动率),用 AbuTLine 封装。
  • ABuTLVwap.calc_vwap(ABuTLVwap.py:33-66):日线级 VWAP。注意源码开头明说"非标准传统意义 vwap"(传统 VWAP 用分钟级数据),这里套用概念:用日级价格(OHLC 均值/中位数/HL 均值/close 四选一)× 成交量,算整体加权均价。返回 AbuTLine,mean 显式设为 vwap。

7.4 ABuTLSimilar 相似度匹配

ABuTLSimilar 提供 calc_similar/calc_similar_top/coint_similar 三个接口,基于 ABuSimilar(find_similar_with_se/find_similar_with_folds/find_similar_with_cnt)做多股票相关性排序。rank_corr_sum 把多个相关矩阵(皮尔逊/斯皮尔曼等)的 rank 求和,综合排序最相似的股票。find_similar_with_folds 被第 8 章 AbuCrossVal 用于策略稳健性验证,也被 AbuPickSimilarNTop 选股因子用于"选与目标最相关的股票"。

本节要点回顾

  1. AbuTLine 构造:fillna(bfill) → arr_to_numpy → 自动算 mean/std/high(=mean+std)/low(=mean-std)/close → _freeze() 冻结。high/low 用 ±1 std 而非 max/min,过滤极值更稳健。
  2. show_kl_pd 一站式:构造 AbuTLine → 依次跑 7 个分析(show/best_poly/shift_distance/regress_channel/skeleton_channel/percents+golden/support_resistance)→ 返回 line 对象。
  3. is_up_trend/is_down_trend:valid_poly(poly=1) 判断能否一次拟合 → calc_regress_deg 算角度 → ≥5° 上升/≤-5° 下降。被 AbuUpDownTrend 买入因子调用。
  4. show_least_valid_poly:最少几次拟合能代表走势。被 AbuLeastPolyWrap 装饰器用作大盘震荡风控(least 超阈值就封锁买入)。
  5. score(LazyFunc):(close-low)/(high-low),high==low 退化 0.8/0.2。LazyFunc 只算一次缓存,后续零开销。
  6. support_resistance_pos + select_k_support_resistance:Chebyshev 多项式(best_poly×长度倍数)→ BFGS 找极值点种子 → set 去重 → KMeans 从 1 到 N 聚类 → 方差阈值(0.06)手肘法选最佳 k。簇心就是阻力/支撑位。
  7. calc_golden 17 字段:视觉(find_golden_point,max/min)+ 统计(find_golden_point_ex,百分位)黄金分割,382/618 是带(below/above),其余比例底部取最低、顶部取最高。被 AbuUpDownGolden 使用。
  8. calc_jump 三层过滤:振幅超日均值(第一层)→ 成交量超月均(第二层)→ 跳空距离超月振幅阈值(第三层)。向上跳空用 low 判、向下用 high 判,保证整根 K 线在缺口侧。
  9. calc_wave_std:对数收益的加权移动 std × √xd,AbuTLine 封装。calc_vwap 是日线级非标准 VWAP(传统用分钟级)。
  10. ABuTLSimilar:基于 ABuSimilar 做多股票相关性排序,find_similar_with_folds 被 AbuCrossVal 用于稳健性验证,被 AbuPickSimilarNTop 用于选相关股。

下一节将解剖 IndicatorBu——ABU 为什么弃用 talib,以及 ATR/MA/布林/MACD/RSI 五大指标的自实现。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U