褚宏光 bf6baa5483 Add scoring module and enhance HTML viewer with standardization
- Add scripts/scoring/ module with normalizer, sensitivity analysis, and config
- Enhance stock_viewer.html with standardized scoring display
- Add integration tests and normalization verification scripts
- Add documentation for standardization implementation and usage guides
- Add data distribution analysis reports for strength scoring dimensions
- Update discussion documents with algorithm optimization plans
2026-01-30 18:43:37 +08:00

24 KiB
Raw Blame History

强度分系统优化方案 - 深度分析

分析日期: 2026-01-29
基于: 18,004个收敛三角形样本的分布分析结果
视角: 量化研究 + 基金经理实战


一、当前系统问题诊断

1.1 分布分析揭示的核心问题

基于18,004个样本的统计分析发现以下关键问题

维度 均值 中位数 超额峰度 核心问题
突破幅度分(up) 0.056 0.000 13.38 中位数=050%数据无信息量
突破幅度分(down) 0.019 0.000 45.72 更极端的零膨胀
成交量分 0.151 0.000 2.77 中位数=0区分度极低
形态规则度 0.052 0.005 4.56 ⚠️ 普遍极低,区分度差
倾斜度分 0.497 0.500 46.33 ⚠️ 75%数据=0.5,无区分度
收敛度分 0.798 0.803 -1.05 相对稳定
价格活跃度 0.069 0.071 -0.25 近正态,最稳定

1.2 问题本质:维度间不可比性

当前等权相加会产生什么后果?

假设等权(各16.67%)后的计算:

强度分 = 1/6 × (0.00 + 0.80 + 0.00 + 0.05 + 0.07 + 0.50)
       = 1/6 × 1.42
       = 0.237

其中:
  突破幅度分(up) = 0.00  (中位数情况)
  收敛度分      = 0.80  (中位数情况)
  成交量分      = 0.00  (中位数情况)
  形态规则度    = 0.05  (中位数情况)
  价格活跃度    = 0.07  (中位数情况)
  倾斜度分      = 0.50  (中位数情况)

问题:收敛度分和倾斜度分"吃掉"了大部分强度分,即使没有任何突破!

1.3 问题根源

  1. 零膨胀分布 (Zero-Inflated):突破幅度分、成交量分的中位数=0
  2. 点质量分布 (Point Mass)倾斜度分75%恰好=0.5
  3. 尺度不一致:各维度的有效取值范围差异巨大
  4. 语义不同步:未突破时,突破幅度分=0是合理的但收敛度分=0.8也是合理的

二、SOTA方法论调研

2.1 多因子量化领域的标准化方法

(1) 截面分位数标准化 (Cross-Sectional Quantile Normalization)

原理: 将每个因子在截面上(同一时点的所有股票)转换为分位数排名

def quantile_normalize(scores):
    """将原始分数转换为百分位排名"""
    ranks = scores.rank(pct=True)  # 百分位排名 [0, 1]
    return ranks

优点:

  • 消除尺度差异
  • 消除偏度和厚尾的影响
  • 各因子变为均匀分布,可直接等权相加

缺点:

  • 丢失绝对信息强突破10%和1%可能排名相同)
  • 对零膨胀分布效果差50%的0会被平分排名

(2) 截面Z-Score标准化

原理: 减去截面均值,除以截面标准差

def zscore_normalize(scores):
    """截面Z-Score标准化"""
    return (scores - scores.mean()) / scores.std()

优点:

  • 保留相对强度信息
  • 简单直观

缺点:

  • 对非正态分布效果差
  • 对极端值敏感
  • 零膨胀分布会导致大量负值

(3) Power Sorting (2023年新方法)

原理: 利用因子-收益关系的非线性特征,通过幂变换捕获不对称性

def power_transform(scores, power=2):
    """非线性幂变换,捕获尾部效应"""
    return np.sign(scores) * np.abs(scores) ** power

优点:

  • 专门处理厚尾分布
  • 保留极端值信息
  • 在多因子策略中表现优于传统方法

参考: Hübner et al. (2023) "Power Sorting", SSRN 4552208

(4) 自适应分组标准化 (Class-Specific Normalization)

原理: 根据数据特性选择不同的标准化策略

def adaptive_normalize(scores, data_type):
    if data_type == 'zero_inflated':
        # 零膨胀: 对非零部分单独标准化
        non_zero = scores[scores > 0]
        return conditional_rank(scores, non_zero)
    elif data_type == 'point_mass':
        # 点质量: 转换为离散分类
        return categorize(scores)
    else:
        # 正常: 标准分位数
        return quantile_normalize(scores)

2.2 控制论视角PID自适应权重

参考: Mehra & Patel (2011) "PID Control for Portfolio Optimization"

将强度分系统视为反馈控制系统:

目标: 最大化风险调整收益
输入: 6个维度的原始得分
控制器: 自适应权重调整
输出: 综合强度分
反馈: 实际交易结果

PID权重调整公式:

def pid_weight_update(w, error, error_integral, error_derivative):
    """
    w: 当前权重
    error: 当前收益偏差
    error_integral: 累积偏差
    error_derivative: 偏差变化率
    """
    Kp, Ki, Kd = 0.1, 0.01, 0.05  # 需要调优
    w_new = w + Kp * error + Ki * error_integral + Kd * error_derivative
    return normalize(w_new)  # 确保权重和为1

优点:

  • 自动适应市场变化
  • 基于实际反馈优化
  • 可解释性强

缺点:

  • 需要足够的历史交易数据
  • 参数调优复杂

2.3 机器学习视角:端到端优化

参考: Chen et al. (2025) "ML Enhanced Multi-Factor Quantitative Trading", arXiv 2507.07107

将"因子得分 → 组合权重 → 交易决策"作为端到端优化问题:

class FactorScoreOptimizer(nn.Module):
    def __init__(self, n_factors=6):
        self.factor_transform = nn.Sequential(
            nn.Linear(n_factors, 32),
            nn.ReLU(),
            nn.Linear(32, n_factors),
            nn.Sigmoid()  # 输出[0,1]
        )
        self.weight_layer = nn.Linear(n_factors, n_factors)
        self.softmax = nn.Softmax(dim=-1)
    
    def forward(self, raw_scores):
        transformed = self.factor_transform(raw_scores)
        weights = self.softmax(self.weight_layer(transformed))
        return (transformed * weights).sum(dim=-1)

优点:

  • 自动学习最优变换和权重
  • 可处理复杂非线性关系

缺点:

  • 黑箱,可解释性差
  • 需要大量标注数据
  • 容易过拟合

三、平滑性优化方案

3.1 推荐方案:分层标准化

针对不同类型的分布,采用不同的标准化策略:

第一层:零膨胀分布处理

适用维度: 突破幅度分、成交量分

def normalize_zero_inflated(scores, name):
    """
    零膨胀分布标准化:
    1. 分离零值和非零值
    2. 非零值进行分位数标准化
    3. 零值赋予基准分0.5(中性)
    """
    is_zero = scores == 0
    is_nonzero = scores > 0
    
    result = pd.Series(index=scores.index, dtype=float)
    
    # 零值 -> 0.5 (中性基准)
    result[is_zero] = 0.5
    
    # 非零值 -> 在0.5~1.0之间排名
    if is_nonzero.sum() > 0:
        nonzero_rank = scores[is_nonzero].rank(pct=True)  # [0, 1]
        result[is_nonzero] = 0.5 + 0.5 * nonzero_rank  # [0.5, 1.0]
    
    return result

效果:

  • 未突破(score=0) → 标准化后=0.5 (中性)
  • 弱突破(score=0.05) → 标准化后≈0.6
  • 强突破(score=0.30) → 标准化后≈0.95

第二层:点质量分布处理

适用维度: 倾斜度分

def normalize_point_mass(scores, center=0.5):
    """
    点质量分布标准化:
    1. 中心值(0.5)保持不变
    2. 偏离中心的值进行拉伸
    """
    deviation = scores - center
    
    # 正偏离和负偏离分别处理
    pos_dev = deviation[deviation > 0]
    neg_dev = deviation[deviation < 0]
    
    result = pd.Series(center, index=scores.index)
    
    if len(pos_dev) > 0:
        # 正偏离 -> 在0.5~1.0之间排名
        result[deviation > 0] = center + 0.5 * pos_dev.rank(pct=True)
    
    if len(neg_dev) > 0:
        # 负偏离 -> 在0.0~0.5之间排名
        result[deviation < 0] = center * neg_dev.rank(pct=True, ascending=False)
    
    return result

第三层:正常分布处理

适用维度: 收敛度分、价格活跃度

def normalize_standard(scores):
    """
    标准分位数标准化:
    直接转换为百分位排名
    """
    return scores.rank(pct=True)

第四层:低区分度分布处理

适用维度: 形态规则度

def normalize_low_variance(scores, expansion_factor=3):
    """
    低区分度分布处理:
    1. 对数变换扩大区分度
    2. 分位数标准化
    """
    # 对数变换扩大小值区间的区分度
    log_scores = np.log1p(scores * expansion_factor)
    return log_scores.rank(pct=True)

3.2 综合标准化流程

def normalize_all_dimensions(df):
    """
    综合标准化流程
    """
    normalized = pd.DataFrame(index=df.index)
    
    # 突破幅度分 - 零膨胀处理
    normalized['price_score_up'] = normalize_zero_inflated(
        df['price_score_up'], 'price_up'
    )
    normalized['price_score_down'] = normalize_zero_inflated(
        df['price_score_down'], 'price_down'
    )
    
    # 成交量分 - 零膨胀处理
    normalized['volume_score'] = normalize_zero_inflated(
        df['volume_score'], 'volume'
    )
    
    # 倾斜度分 - 点质量处理
    normalized['tilt_score'] = normalize_point_mass(
        df['tilt_score'], center=0.5
    )
    
    # 收敛度分 - 标准处理
    normalized['convergence_score'] = normalize_standard(
        df['convergence_score']
    )
    
    # 价格活跃度 - 标准处理
    normalized['activity_score'] = normalize_standard(
        df['activity_score']
    )
    
    # 形态规则度 - 低区分度处理
    normalized['geometry_score'] = normalize_low_variance(
        df['geometry_score']
    )
    
    return normalized

3.3 标准化后的等权计算

def calculate_strength_equal_weight(normalized_df, direction='up'):
    """
    等权强度分计算 (标准化后)
    
    参数:
        direction: 'up' 或 'down',决定使用哪个突破幅度分
    """
    if direction == 'up':
        price_score = normalized_df['price_score_up']
    else:
        price_score = normalized_df['price_score_down']
    
    # 等权: 各1/6
    strength = (
        price_score / 6 +
        normalized_df['convergence_score'] / 6 +
        normalized_df['volume_score'] / 6 +
        normalized_df['geometry_score'] / 6 +
        normalized_df['activity_score'] / 6 +
        normalized_df['tilt_score'] / 6
    )
    
    return strength

四、参数可调性设计

4.1 应用层接口设计

为基金经理提供可解释、可调节的参数接口:

class StrengthScoreConfig:
    """强度分配置 - 应用层接口"""
    
    def __init__(self):
        # 权重参数 (默认等权)
        self.weight_price = 1/6
        self.weight_convergence = 1/6
        self.weight_volume = 1/6
        self.weight_geometry = 1/6
        self.weight_activity = 1/6
        self.weight_tilt = 1/6
        
        # 阈值参数
        self.price_threshold = 0.6      # 突破幅度分阈值(标准化后)
        self.convergence_threshold = 0.7 # 收敛度分阈值
        self.volume_threshold = 0.5      # 成交量分阈值(设为中性)
        
        # 筛选模式
        self.filter_mode = 'and'  # 'and' 或 'or'
        
        # 方向偏好
        self.direction_preference = 'both'  # 'up', 'down', 'both'
    
    def set_aggressive(self):
        """激进模式: 重视突破"""
        self.weight_price = 0.35
        self.weight_volume = 0.25
        self.weight_convergence = 0.15
        self.weight_geometry = 0.10
        self.weight_activity = 0.10
        self.weight_tilt = 0.05
    
    def set_conservative(self):
        """保守模式: 重视形态质量"""
        self.weight_price = 0.15
        self.weight_convergence = 0.30
        self.weight_volume = 0.10
        self.weight_geometry = 0.20
        self.weight_activity = 0.20
        self.weight_tilt = 0.05
    
    def set_volume_focus(self):
        """放量模式: 重视成交量确认"""
        self.weight_price = 0.25
        self.weight_volume = 0.35
        self.weight_convergence = 0.15
        self.weight_geometry = 0.10
        self.weight_activity = 0.10
        self.weight_tilt = 0.05

4.2 多维度筛选器

class MultiDimensionFilter:
    """多维度筛选器"""
    
    def __init__(self, config: StrengthScoreConfig):
        self.config = config
    
    def filter(self, df):
        """
        根据配置筛选信号
        
        返回:
            满足条件的行索引
        """
        conditions = []
        
        # 突破幅度条件
        if self.config.direction_preference in ['up', 'both']:
            conditions.append(
                df['price_score_up_normalized'] >= self.config.price_threshold
            )
        if self.config.direction_preference in ['down', 'both']:
            conditions.append(
                df['price_score_down_normalized'] >= self.config.price_threshold
            )
        
        # 收敛度条件
        conditions.append(
            df['convergence_score_normalized'] >= self.config.convergence_threshold
        )
        
        # 成交量条件 (可选)
        if self.config.volume_threshold > 0.5:  # 只有设置高于中性时才过滤
            conditions.append(
                df['volume_score_normalized'] >= self.config.volume_threshold
            )
        
        # 组合条件
        if self.config.filter_mode == 'and':
            final_condition = conditions[0]
            for cond in conditions[1:]:
                final_condition = final_condition & cond
        else:  # 'or'
            final_condition = conditions[0]
            for cond in conditions[1:]:
                final_condition = final_condition | cond
        
        return df[final_condition].index

4.3 敏感性分析工具

def sensitivity_analysis(df, config, param_name, param_range):
    """
    参数敏感性分析
    
    示例: 分析 price_threshold 从 0.5 到 0.9 的影响
    """
    results = []
    
    for value in param_range:
        # 设置参数
        setattr(config, param_name, value)
        
        # 筛选
        filter = MultiDimensionFilter(config)
        selected = filter.filter(df)
        
        # 统计
        results.append({
            'param_value': value,
            'n_signals': len(selected),
            'pct_selected': len(selected) / len(df) * 100,
            # 可添加更多指标: 平均收益、胜率等
        })
    
    return pd.DataFrame(results)

五、基金经理视角:各维度量化意义

5.1 突破幅度分 - 信号强度

量化意义:

  • 衡量价格动能的核心指标
  • 反映市场对突破的认可程度
  • 是最直接的交易信号

实战应用:

  • >P90: 强信号,可考虑立即入场
  • P75-P90: 中等信号,需配合其他确认
  • <P75: 弱信号,观望为主

当前问题:

  • 中位数=0导致大量信号被"埋没"
  • 建议:标准化后使用,而非原始值

基金经理关注点:

"突破幅度多大才值得交易?我需要一个可量化的入场标准。"

建议阈值:

  • 激进策略: 标准化后 > 0.60
  • 稳健策略: 标准化后 > 0.75
  • 保守策略: 标准化后 > 0.85

5.2 收敛度分 - 蓄势程度

量化意义:

  • 衡量多空博弈的激烈程度
  • 收敛越紧,突破后动能越大
  • 反映市场分歧逐渐收窄的过程

实战应用:

  • 高收敛(>0.85): 能量积蓄充分,突破概率高
  • 中收敛(0.70-0.85): 标准形态
  • 低收敛(<0.70): 形态不成熟,信号可靠性低

当前评价:

  • 分布稳定,是最可靠的维度
  • 区分度好,有实际筛选价值
  • 建议:可适当提高权重

基金经理关注点:

"收敛程度是否与后续涨幅相关?越收敛越好吗?"

实证建议: 收敛度与突破成功率正相关,但与突破幅度的关系需进一步回测验证

5.3 成交量分 - 资金确认

量化意义:

  • 衡量资金介入的强度
  • 放量突破更具可持续性
  • 是区分真突破和假突破的关键

实战应用:

  • 高放量(>P80): 资金确认,信号可信度高
  • 中等放量(P50-P80): 一般确认
  • 无放量(<P50): 存疑,可能是假突破

当前问题:

  • 中位数=050%三角形无放量信息
  • 作为必要条件会过滤太多信号
  • 建议:降权或作为"加分项"而非"扣分项"

基金经理关注点:

"没有放量的突破能信吗?放量标准应该是多少倍?"

建议策略:

  • 不作为必要条件会丢失50%潜在机会)
  • 作为信号评级的加分项
  • 放量突破 → 加仓或延长持有期

5.4 形态规则度 - 形态质量

量化意义:

  • 衡量形态的几何标准性
  • 高规则度意味着市场对关键价位有共识
  • 反映形态是否"教科书级别"

实战应用:

  • 高规则度(>P80): 教科书形态,交易员容易识别
  • 中规则度(P50-P80): 标准形态
  • 低规则度(<P50): 非典型,不易被市场认可

当前问题:

  • ⚠️ 普遍极低中位数0.005),区分度差
  • ⚠️ 可能是算法对"规则度"的定义过于严格
  • 建议:对数变换扩大区分度,或重新定义计算方式

基金经理关注点:

"形态越标准越好吗?非标准形态是否也有交易价值?"

思考: 过于"完美"的形态可能是市场共识过高,反而需要警惕。建议:

  • 规则度作为辅助参考,权重不宜过高
  • 非标准形态不应直接排除

5.5 价格活跃度 - 真实博弈

量化意义:

  • 衡量价格振荡充分性
  • 区分真实博弈 vs 僵尸形态
  • 反映市场参与度

实战应用:

  • 高活跃度(>P75): 市场参与充分,形态有效
  • 中活跃度(P25-P75): 正常情况
  • 低活跃度(<P25): 僵尸形态,可能是流动性问题

当前评价:

  • 近正态分布,最稳定的维度
  • 有实际区分价值
  • 建议:可适当提高权重

基金经理关注点:

"如何区分'健康的收敛'和'无人问津的死股'"

价格活跃度正是解决这个问题的关键指标

5.6 倾斜度分 - 趋势一致性

量化意义:

  • 衡量突破方向与形态趋势的一致性
  • 顺势突破更可靠,逆势突破需谨慎
  • 区分上升/下降/对称三角形

实战应用:

  • 高倾斜度(>0.6): 强趋势一致,高置信度
  • 中性(≈0.5): 对称三角形,方向不明确
  • 低倾斜度(<0.4): 逆势突破,需额外确认

当前问题:

  • 75%的值=0.5,几乎无区分度
  • 算法强烈偏好对称三角形
  • 建议:重新标准化或调整算法参数

基金经理关注点:

"上升三角形向上突破 vs 下降三角形向上突破,胜率有差异吗?"

实证建议: 需要回测验证,但直觉上顺势突破应该更可靠


六、整体刻画准确性评估

6.1 当前系统的优点

  1. 覆盖维度全面:

    • 价格维度: 突破幅度、收敛度
    • 量能维度: 成交量
    • 形态维度: 几何规则度、倾斜度
    • 行为维度: 价格活跃度
  2. 归一化方式合理:

    • 所有维度输出在[0, 1]区间
    • 使用tanh、exp等非线性变换
  3. 权重可配置:

    • 代码支持权重参数调整
    • 突破幅度作为主要权重是合理的

6.2 当前系统的不足

  1. 维度间不可比:

    • 原始得分直接加权,未考虑分布差异
    • 中位数差异巨大(0 vs 0.8)
  2. 零膨胀问题未处理:

    • 突破幅度分、成交量分50%为0
    • 这些0值参与加权计算会稀释信号
  3. 点质量问题未处理:

    • 倾斜度分75%为0.5
    • 无法区分真正的对称三角形和算法偏好
  4. 缺乏截面标准化:

    • 未考虑同一时点不同股票的相对排名
    • 强度分的绝对值缺乏参照系

6.3 刻画准确性评分

评估维度 当前评分 优化后预期
维度完整性 (5/5) 5/5
归一化合理性 ☆☆ (3/5) 4/5
维度可比性 ☆☆☆ (2/5) 4/5
分布平滑性 ☆☆☆ (2/5) 4/5
参数可调性 ☆☆ (3/5) 5/5
实战可用性 ☆☆ (3/5) 4/5
综合评分 2.8/5 4.3/5

七、优化建议总结

7.1 短期优化(立即可做)

  1. 实施分层标准化

    • 对突破幅度分、成交量分使用零膨胀处理
    • 对倾斜度分使用点质量处理
    • 对其他维度使用分位数标准化
  2. 调整权重分配

    当前 → 建议(等权基础):
    突破幅度分: 45% → 16.67% (标准化后等权)
    收敛度分: 15% → 16.67%
    成交量分: 10% → 16.67%
    形态规则度: 10% → 16.67%
    价格活跃度: 15% → 16.67%
    倾斜度分: 5% → 16.67%
    
  3. 提供预设配置

    • 激进模式、保守模式、放量模式等
    • 让用户可以快速切换

7.2 中期优化(需要开发)

  1. 截面标准化

    • 在每个时间点对所有股票排名
    • 提供"相对强度"而非"绝对强度"
  2. 敏感性分析工具

    • 参数变化对信号数量的影响
    • 参数变化对回测收益的影响
  3. 动态权重调整

    • 基于市场状态自动调整权重
    • 牛市/熊市/震荡市不同配置

7.3 长期优化(研究方向)

  1. 机器学习优化

    • 端到端学习最优变换和权重
    • 需要构建完整的回测框架
  2. 控制论框架

    • PID自适应权重调整
    • 基于实际交易反馈优化
  3. 因子有效性验证

    • 各维度与未来收益的IC分析
    • 剔除无效因子,保留有效因子

八、代码实现建议

8.1 目录结构

technical-patterns-lab/
├── src/
│   ├── scoring/
│   │   ├── normalizer.py       # 标准化模块
│   │   ├── strength_score.py   # 强度分计算
│   │   ├── config.py           # 配置管理
│   │   └── filter.py           # 多维度筛选
│   └── analysis/
│       ├── sensitivity.py      # 敏感性分析
│       └── backtest.py         # 回测框架
└── configs/
    ├── aggressive.yaml         # 激进配置
    ├── conservative.yaml       # 保守配置
    └── volume_focus.yaml       # 放量配置

8.2 核心类设计

# scoring/normalizer.py
class FactorNormalizer:
    """因子标准化器"""
    
    def normalize_zero_inflated(self, series): ...
    def normalize_point_mass(self, series): ...
    def normalize_standard(self, series): ...
    def normalize_low_variance(self, series): ...
    
    def normalize_all(self, df) -> pd.DataFrame: ...

# scoring/strength_score.py
class StrengthScoreCalculator:
    """强度分计算器"""
    
    def __init__(self, config: Config):
        self.config = config
        self.normalizer = FactorNormalizer()
    
    def calculate(self, raw_df) -> pd.Series: ...
    def calculate_with_details(self, raw_df) -> pd.DataFrame: ...

# scoring/filter.py
class MultiDimensionFilter:
    """多维度筛选器"""
    
    def filter(self, df, config) -> pd.Index: ...
    def filter_top_n(self, df, n, config) -> pd.Index: ...

九、下一步行动计划

立即行动

  1. 实现分层标准化函数
  2. 创建等权强度分计算
  3. 提供3种预设配置

短期计划

  1. 📝 实现敏感性分析工具
  2. 📝 对标准化后的数据重新进行分布分析
  3. 📝 验证标准化效果

中期计划

  1. 🔬 实现截面标准化
  2. 🔬 构建简单回测框架
  3. 🔬 验证各维度的预测能力

长期研究

  1. 🔮 探索机器学习优化
  2. 🔮 探索控制论框架
  3. 🔮 发表研究报告

参考文献

  1. Hübner et al. (2023). "Power Sorting". SSRN 4552208.
  2. Chen et al. (2025). "Machine Learning Enhanced Multi-Factor Quantitative Trading". arXiv 2507.07107.
  3. Mehra & Patel (2011). "PID Control for Portfolio Optimization". InTech.
  4. Lo et al. (2000). "Foundations of Technical Analysis". SSRN 228099.

报告创建时间: 2026-01-29
作者: AI量化研究助手
版本: v1.0