价格走势角度分析:用于预测金融市场的混合模型·进阶篇
⛰️

价格走势角度分析:用于预测金融市场的混合模型·进阶篇

(2/3)·从纸质图表的模糊角线到机器学习可读的连续价格角度,差的不是经验而是算法

案例拆解新手友好 第 2/3 篇
很多交易者把趋势线斜率当角度分析的全部,结果在同一张图上画出互相矛盾的方向。江恩角线依赖纸质比例,直接搬进数字终端就会失真。把连续价格点夹角当成装饰而非信号,等于带着地形图却闭眼爬山。

「给角度序列贴上未来标签」

做价格角度的机器学习,第一步不是训模型,而是先把‘未来’写进表里。下面这段 Python 把每个 bar 往后推 24 根(prediction_period=24),比较那时的 close 与当前 close:涨了标 1,跌了标 0,塞进 future_direction 列;同时算百分比变动 future_change_pct,正为负都留痕。 标注逻辑很直白:够不到 24 根未来的样本直接填 np.nan,不强行猜。外汇与贵金属波动受杠杆放大,24 根 H1 就是一天,这种中短窗在黄金上常出现 0.3%–1.2% 的 future_change_pct 跨度,但方向标签噪声也不小。 prepare_features 接着用 lookback=15 滑窗,把连续 15 个 angle 摊成 angle_0…angle_14 的特征字典,再补 angle_mean、angle_std、angle_min 等衍生量。样本不足 lookback 就直接返回 None,避免在 MT5 导出的短样本上瞎跑。 小布盯盘里若接了这套角度流,建议先把 lookback 从 15 调到 30 试一周,看黄金 M15 上 future_direction 的标签翻转频率是否降下来。

MQL5 / C++
def add_future_price_info(angular_df, prediction_period=class="num">24):
    # Add future price direction
    future_directions = []
    for i in range(len(angular_df)):
        if i + prediction_period < len(angular_df):
            # class="num">1 = growth, class="num">0 = fall
            future_dir = class="num">1 if angular_df[&class="macro">#x27;close&class="macro">#x27;].iloc[i + prediction_period] > angular_df[&class="macro">#x27;close&class="macro">#x27;].iloc[i] else class="num">0
            future_directions.append(future_dir)
        else:
            future_directions.append(np.nan)
    
    angular_df[&class="macro">#x27;future_direction&class="macro">#x27;] = future_directions
    
    # Calculate magnitude of the future change(in percent)
    future_changes = []
    for i in range(len(angular_df)):
        if i + prediction_period < len(angular_df):
            pct_change = (angular_df[&class="macro">#x27;close&class="macro">#x27;].iloc[i + prediction_period] - angular_df[&class="macro">#x27;close&class="macro">#x27;].iloc[i]) / angular_df[&class="macro">#x27;close&class="macro">#x27;].iloc[i] * class="num">100
            future_changes.append(pct_change)
        else:
            future_changes.append(np.nan)
    
    angular_df[&class="macro">#x27;future_change_pct&class="macro">#x27;] = future_changes
    
    class="kw">return angular_df
def prepare_features(angular_df, lookback=class="num">15):
    features = []
    targets_class = []  # For classification(direction)
    targets_reg = []    # For regression(percent change)
    
    # Discard strings with NaN
    filtered_df = angular_df.dropna(subset=[&class="macro">#x27;angle&class="macro">#x27;, &class="macro">#x27;future_direction&class="macro">#x27;, &class="macro">#x27;future_change_pct&class="macro">#x27;])
    
    # Check if there is enough data
    if len(filtered_df) <= lookback:
        print("Not enough data for analysis")
        class="kw">return None, None, None
    
    for i in range(lookback, len(filtered_df)):
        # Get latest lookback of bars
        window = filtered_df.iloc[i-lookback:i]
        
        # Take last angles as a sequence
        feature_dict = {
            f&class="macro">#x27;angle_{j}&class="macro">#x27;: window[&class="macro">#x27;angle&class="macro">#x27;].iloc[j] for j in range(lookback)
        }
        
        # Add derivative characteristics of angles
        feature_dict.update({
            &class="macro">#x27;angle_mean&class="macro">#x27;: window[&class="macro">#x27;angle&class="macro">#x27;].mean(),
            &class="macro">#x27;angle_std&class="macro">#x27;: window[&class="macro">#x27;angle&class="macro">#x27;].std(),
            &class="macro">#x27;angle_min&class="macro">#x27;: window[&class="macro">#x27;angle&class="macro">#x27;].min(),

窗口特征怎么一次性抽出来

这段逻辑干的事很直接:在一个滚动窗口里把角度、价格、成交量三类特征一次性算完,再塞进列表等模型消费。角度部分除了取窗口内最大值,还分别算了最后1根、最近3根、5根、10根均值,等于同时看瞬时斜率和短期斜率惯性。 positive_angles_ratio 用 (angle>0).mean() 给出窗口内正角度占比,能快速判断这段走势是多头倾斜主导还是空头倾斜主导。价格端给了标准差、区间涨跌幅和高低价相对收盘的区间百分比,波动与空间一眼可辨。 成交量特征里 tick_volume_ratio 做了除零保护:均值大于0才除,否则返回1,避免回测时直接崩。最后三行把特征字典追加进 features,并把未来方向、未来涨跌幅分别写入分类与回归目标数组,函数整体 return 出 DataFrame 和两只 numpy 数组,可直接喂给 sklearn。 开 MT5 导出 tick 数据跑一遍这段,重点看 angle_last_10_mean 与 positive_angles_ratio 在同周期黄金15分钟图上的分布,外汇与贵金属杠杆高,信号仅作概率参考。

MQL5 / C++
&class="macro">#x27;angle_max&class="macro">#x27;: window[&class="macro">#x27;angle&class="macro">#x27;].max(),
&class="macro">#x27;angle_last&class="macro">#x27;: window[&class="macro">#x27;angle&class="macro">#x27;].iloc[-class="num">1],
&class="macro">#x27;angle_last_3_mean&class="macro">#x27;: window[&class="macro">#x27;angle&class="macro">#x27;].iloc[-class="num">3:].mean(),
&class="macro">#x27;angle_last_5_mean&class="macro">#x27;: window[&class="macro">#x27;angle&class="macro">#x27;].iloc[-class="num">5:].mean(),
&class="macro">#x27;angle_last_10_mean&class="macro">#x27;: window[&class="macro">#x27;angle&class="macro">#x27;].iloc[-class="num">10:].mean(),
&class="macro">#x27;positive_angles_ratio&class="macro">#x27;: (window[&class="macro">#x27;angle&class="macro">#x27;] > class="num">0).mean(),
&class="macro">#x27;current_price&class="macro">#x27;: window[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1],
&class="macro">#x27;price_std&class="macro">#x27;: window[&class="macro">#x27;close&class="macro">#x27;].std(),
&class="macro">#x27;price_change_pct&class="macro">#x27;: (window[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1] - window[&class="macro">#x27;close&class="macro">#x27;].iloc[class="num">0]) / window[&class="macro">#x27;close&class="macro">#x27;].iloc[class="num">0] * class="num">100,
&class="macro">#x27;high_low_range&class="macro">#x27;: (window[&class="macro">#x27;high&class="macro">#x27;].max() - window[&class="macro">#x27;low&class="macro">#x27;].min()) / window[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1] * class="num">100,
&class="macro">#x27;last_tick_volume&class="macro">#x27;: window[&class="macro">#x27;tick_volume&class="macro">#x27;].iloc[-class="num">1],
&class="macro">#x27;avg_tick_volume&class="macro">#x27;: window[&class="macro">#x27;tick_volume&class="macro">#x27;].mean(),
&class="macro">#x27;tick_volume_ratio&class="macro">#x27;: window[&class="macro">#x27;tick_volume&class="macro">#x27;].iloc[-class="num">1] / window[&class="macro">#x27;tick_volume&class="macro">#x27;].mean() if window[&class="macro">#x27;tick_volume&class="macro">#x27;].mean() > class="num">0 else class="num">1,
})

features.append(feature_dict)
targets_class.append(filtered_df.iloc[i][&class="macro">#x27;future_direction&class="macro">#x27;])
targets_reg.append(filtered_df.iloc[i][&class="macro">#x27;future_change_pct&class="macro">#x27;])

class="kw">return pd.DataFrame(features), np.array(targets_class), np.array(targets_reg)

◍ 双模型拆解价格角度的涨跌与幅度

把角度特征喂给模型时,单用一个网络往往只能猜方向,幅度信息就丢了。这里拆成两个 CatBoost 模型并行跑:分类器判涨跌方向,回归器估后续波动的百分比幅度,两者拼起来才是一个带概率和量级的标准信号。 训练函数里数据按 0.3 比例切分测试集,random_state 锁死 42 保证可复现;两个模型都设 500 轮、学习率 0.03、深度 6,分类用 Logloss、回归用 RMSE,早停轮数 50。跑完会直接打印分类准确率和回归 RMSE,并把特征重要性前 5 行吐出来。 实盘调用时,predict_future_movement 默认取最近 15 根的角度数据做推理。外汇与贵金属杠杆高、滑点突发行情多,模型输出仅代表历史模式下的倾向概率,不代表未来必现,开 MT5 接自己的品种前先跑一轮回测看准确率掉没掉。

MQL5 / C++
from catboost class="kw">import CatBoostClassifier, CatBoostRegressor
from sklearn.model_selection class="kw">import train_test_split
from sklearn.metrics class="kw">import accuracy_score, mean_squared_error
def train_hybrid_model(X, y_class, y_reg, test_size=class="num">0.3):
    # Splitting data into training and test
    X_train, X_test, y_class_train, y_class_test, y_reg_train, y_reg_test = train_test_split(
        X, y_class, y_reg, test_size=test_size, random_state=class="num">42, shuffle=True
    )
    
    # Parameters for classification model
    params_class = {
        &class="macro">#x27;iterations&class="macro">#x27;: class="num">500,
        &class="macro">#x27;learning_rate&class="macro">#x27;: class="num">0.03,
        &class="macro">#x27;depth&class="macro">#x27;: class="num">6,
        &class="macro">#x27;loss_function&class="macro">#x27;: &class="macro">#x27;Logloss&class="macro">#x27;,
        &class="macro">#x27;random_seed&class="macro">#x27;: class="num">42,
        &class="macro">#x27;verbose&class="macro">#x27;: False
    }
    
    # Parameters for regression model
    params_reg = {
        &class="macro">#x27;iterations&class="macro">#x27;: class="num">500,
        &class="macro">#x27;learning_rate&class="macro">#x27;: class="num">0.03,
        &class="macro">#x27;depth&class="macro">#x27;: class="num">6,
        &class="macro">#x27;loss_function&class="macro">#x27;: &class="macro">#x27;RMSE&class="macro">#x27;,
        &class="macro">#x27;random_seed&class="macro">#x27;: class="num">42,
        &class="macro">#x27;verbose&class="macro">#x27;: False
    }
    
    # Training classification model(directional prediction)
    print("Training classification model...")
    model_class = CatBoostClassifier(**params_class)
    model_class.fit(X_train, y_class_train, eval_set=(X_test, y_class_test),
                        early_stopping_rounds=class="num">50, verbose=False)
    
    # Checking classification accuracy
    y_class_pred = model_class.predict(X_test)
    accuracy = accuracy_score(y_class_test, y_class_pred)
    print(f"Classification accuracy: {accuracy:.4f} ({accuracy*class="num">100:.2f}%)")
    
    # Training regression model(forecast of percentage change)
    print("\nTraining regression model...")
    model_reg = CatBoostRegressor(**params_reg)
    model_reg.fit(X_train, y_reg_train, eval_set=(X_test, y_reg_test),
                        early_stopping_rounds=class="num">50, verbose=False)
    
    # Checking regression accuracy
    y_reg_pred = model_reg.predict(X_test)
    rmse = np.sqrt(mean_squared_error(y_reg_test, y_reg_pred))
    print(f"RMSE regressions: {rmse:.4f}")
    
    # Print importance of features
    print("\nImportance of features for classification:")
    feature_importance = model_class.get_feature_importance(prettified=True)
    print(feature_importance.head(class="num">5))
    
    class="kw">return model_class, model_reg
def predict_future_movement(model_class, model_reg, angular_df, lookback=class="num">15):
    # Get latest data

「把角度序列喂给模型前的特征拼装」

预测之前先卡一道数据量门槛:若可用 K 线不足 lookback 根,直接打印提示并返回 None,避免后续切片越界。这一步在外汇与贵金属这种高波动品种上尤其必要,样本不够时模型输出可信度倾向骤降。 拿到最近 lookback 根 K 线后,先把每根的角度按索引展平成 angle_0 到 angle_{lookback-1} 的扁平特征,保证和训练时的输入维度完全一致。任何维度错位都会让 sklearn 模型在 predict 时直接抛异常。 衍生统计量是这套特征工程的核心:除了 angle_mean/std/min/max,还单独取了最近 3、5、10 根的角度均值,用来刻画短期斜率的加速或钝化。positive_angles_ratio 给出窗口内正角度占比,若接近 1 说明多头推力连续占优,可能暗示趋势延续。 价格侧补了 current_price、price_std、price_change_pct(首尾收盘价百分比差)以及 high_low_range(高低极差占末价百分比)。成交量侧用 last_tick_volume 与 avg_tick_volume 的比值捕捉放量异动——比值显著大于 1 时,价格突破倾向更值得关注,但外汇杠杆交易高风险,仍需结合止损。 最后把字典转成单行 DataFrame 送进分类与回归模型:分类给方向概率与 UP/DOWN 标签,回归给 change_pct 幅度预估。注意 probability 取的是预测类别对应的那一项,不是固定取索引 1。

MQL5 / C++
if len(angular_df) < lookback:
    print("Not enough data for forecast")
    class="kw">return None

# Get latest lookback of bars
last_window = angular_df.tail(lookback)

# Form features as during training
feature_dict = {
    f&class="macro">#x27;angle_{j}&class="macro">#x27;: last_window[&class="macro">#x27;angle&class="macro">#x27;].iloc[j] for j in range(lookback)
}

# Add derivative characteristics
feature_dict.update({
    &class="macro">#x27;angle_mean&class="macro">#x27;: last_window[&class="macro">#x27;angle&class="macro">#x27;].mean(),
    &class="macro">#x27;angle_std&class="macro">#x27;: last_window[&class="macro">#x27;angle&class="macro">#x27;].std(),
    &class="macro">#x27;angle_min&class="macro">#x27;: last_window[&class="macro">#x27;angle&class="macro">#x27;].min(),
    &class="macro">#x27;angle_max&class="macro">#x27;: last_window[&class="macro">#x27;angle&class="macro">#x27;].max(),
    &class="macro">#x27;angle_last&class="macro">#x27;: last_window[&class="macro">#x27;angle&class="macro">#x27;].iloc[-class="num">1],
    &class="macro">#x27;angle_last_3_mean&class="macro">#x27;: last_window[&class="macro">#x27;angle&class="macro">#x27;].iloc[-class="num">3:].mean(),
    &class="macro">#x27;angle_last_5_mean&class="macro">#x27;: last_window[&class="macro">#x27;angle&class="macro">#x27;].iloc[-class="num">5:].mean(),
    &class="macro">#x27;angle_last_10_mean&class="macro">#x27;: last_window[&class="macro">#x27;angle&class="macro">#x27;].iloc[-class="num">10:].mean(),
    &class="macro">#x27;positive_angles_ratio&class="macro">#x27;: (last_window[&class="macro">#x27;angle&class="macro">#x27;] > class="num">0).mean(),
    &class="macro">#x27;current_price&class="macro">#x27;: last_window[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1],
    &class="macro">#x27;price_std&class="macro">#x27;: last_window[&class="macro">#x27;close&class="macro">#x27;].std(),
    &class="macro">#x27;price_change_pct&class="macro">#x27;: (last_window[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1] - last_window[&class="macro">#x27;close&class="macro">#x27;].iloc[class="num">0]) / last_window[&class="macro">#x27;close&class="macro">#x27;].iloc[class="num">0] * class="num">100,
    &class="macro">#x27;high_low_range&class="macro">#x27;: (last_window[&class="macro">#x27;high&class="macro">#x27;].max() - last_window[&class="macro">#x27;low&class="macro">#x27;].min()) / last_window[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1] * class="num">100,
    &class="macro">#x27;last_tick_volume&class="macro">#x27;: last_window[&class="macro">#x27;tick_volume&class="macro">#x27;].iloc[-class="num">1],
    &class="macro">#x27;avg_tick_volume&class="macro">#x27;: last_window[&class="macro">#x27;tick_volume&class="macro">#x27;].mean(),
    &class="macro">#x27;tick_volume_ratio&class="macro">#x27;: last_window[&class="macro">#x27;tick_volume&class="macro">#x27;].iloc[-class="num">1] / last_window[&class="macro">#x27;tick_volume&class="macro">#x27;].mean() if last_window[&class="macro">#x27;tick_volume&class="macro">#x27;].mean() > class="num">0 else class="num">1,
})

# Convert to format for model
X_pred = pd.DataFrame([feature_dict])

# Model predictions
direction_proba = model_class.predict_proba(X_pred)[class="num">0]
direction = model_class.predict(X_pred)[class="num">0]
change_pct = model_reg.predict(X_pred)[class="num">0]

# Form result
result = {
    &class="macro">#x27;direction&class="macro">#x27;: &class="macro">#x27;UP&class="macro">#x27; if direction == class="num">1 else &class="macro">#x27;DOWN&class="macro">#x27;,
    &class="macro">#x27;probability&class="macro">#x27;: direction_proba[class="type">int(direction)],
    &class="macro">#x27;change_pct&class="macro">#x27;: change_pct,

信号分级的阈值逻辑

这段 Python 片段把模型输出转成了可执行的交易信号,核心在概率与幅度双过滤。方向分类概率突破 0.7 且变动幅度超 0.5% 才给 STRONG 级别,否则仅在 0.6 以上给普通 BUY/SELL,其余一律 NEUTRAL。 current_price 取最近一根收盘,predicted_price 用变动百分比线性外推,意味着信号强度直接挂钩模型对「涨跌幅」的置信。外汇与贵金属杠杆高,这类阈值只是概率倾向,实盘需结合价差与滑点重估。 把 0.7 / 0.5 这两个硬门槛调低,信号会变密但假突破概率可能上升;调高则更稀却更挑行情。开 MT5 用历史tick回测自己的品种,看哪组阈值在欧美盘口损耗最小。

MQL5 / C++
    &class="macro">#x27;current_price&class="macro">#x27;: last_window[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1],
    &class="macro">#x27;predicted_price&class="macro">#x27;: last_window[&class="macro">#x27;close&class="macro">#x27;].iloc[-class="num">1] * (class="num">1 + change_pct/class="num">100),
    }
    
    # Form signal
    if direction == class="num">1 and direction_proba[class="num">1] > class="num">0.7 and change_pct > class="num">0.5:
        result[&class="macro">#x27;signal&class="macro">#x27;] = &class="macro">#x27;STRONG_BUY&class="macro">#x27;
    elif direction == class="num">1 and direction_proba[class="num">1] > class="num">0.6:
        result[&class="macro">#x27;signal&class="macro">#x27;] = &class="macro">#x27;BUY&class="macro">#x27;
    elif direction == class="num">0 and direction_proba[class="num">0] > class="num">0.7 and change_pct < -class="num">0.5:
        result[&class="macro">#x27;signal&class="macro">#x27;] = &class="macro">#x27;STRONG_SELL&class="macro">#x27;
    elif direction == class="num">0 and direction_proba[class="num">0] > class="num">0.6:
        result[&class="macro">#x27;signal&class="macro">#x27;] = &class="macro">#x27;SELL&class="macro">#x27;
    else:
        result[&class="macro">#x27;signal&class="macro">#x27;] = &class="macro">#x27;NEUTRAL&class="macro">#x27;
    
    class="kw">return result

◍ 用历史K线给角度策略做回测

光看角度回归的理论曲线不够,得丢进历史数据里跑一遍才知道信号靠不靠谱。下面这段回测函数把含角度列的 DataFrame 接进去,用前视 24 根 K 线作为预测 horizon,对每一根决策 K 线调用已有的分类与回归模型拿信号。 函数核心循环从 lookback=15 开始,避开了前段数据不足的区域;只保留 angle 非空的行,逐根取截至当前的窗口做预测。若模型返回 BUY/STRONG_BUY 记 1,SELL/STRONG_SELL 记 -1,其余记 0,同时把 24 根后的收盘价变动百分比作为实际盈亏基准存下来。 跑完之后用 numpy 算 active 信号的 PnL:胜率是盈利笔数占总笔数比例,avg_win 与 avg_loss 分别是盈亏均值,profit_factor 取盈利总和绝对值除以亏损总和。外汇与贵金属波动大、杠杆高,回测盈利不代表实盘能复现,信号失效概率始终存在。 你直接把这段贴进 MT5 的 Python 环境(或本地 Jupyter 接 MT5 导出的 angular_df),把 lookback 调到 20 或 30,看 win_rate 和 profit_factor 怎么跳,比盯论坛参数帖实在。

MQL5 / C++
def backtest_strategy(angular_df, model_class, model_reg, lookback=class="num">15):
    # Filter data
    clean_df = angular_df.dropna(subset=[&class="macro">#x27;angle&class="macro">#x27;])
    
    # To store results
    signals = []
    actual_changes = []
    timestamps = []
    
    # Modelling trading based on historical data
    for i in range(lookback, len(clean_df) - class="num">24):  # class="num">24 bars - forecast horizon
        # Data at the time of decision
        window_df = clean_df.iloc[:i]
        
        # Get prediction
        prediction = predict_future_movement(model_class, model_reg, window_df, lookback)
        
        if prediction:
            # Record signal(class="num">1 = buy, -class="num">1 = sell, class="num">0 = neutral)
            if prediction[&class="macro">#x27;signal&class="macro">#x27;] in [&class="macro">#x27;BUY&class="macro">#x27;, &class="macro">#x27;STRONG_BUY&class="macro">#x27;]:
                signals.append(class="num">1)
            elif prediction[&class="macro">#x27;signal&class="macro">#x27;] in [&class="macro">#x27;SELL&class="macro">#x27;, &class="macro">#x27;STRONG_SELL&class="macro">#x27;]:
                signals.append(-class="num">1)
            else:
                signals.append(class="num">0)
            
            # Record actual change
            actual_change = (clean_df.iloc[i+class="num">24][&class="macro">#x27;close&class="macro">#x27;] - clean_df.iloc[i][&class="macro">#x27;close&class="macro">#x27;]) / clean_df.iloc[i][&class="macro">#x27;close&class="macro">#x27;] * class="num">100
            actual_changes.append(actual_change)
            
            # Record time
            timestamps.append(clean_df.iloc[i][&class="macro">#x27;time&class="macro">#x27;])
    
    # Result analysis
    signals = np.array(signals)
    actual_changes = np.array(actual_changes)
    
    # Calculate P&L for signals(except neutral ones)
    active_signals = signals != class="num">0
    pnl = signals[active_signals] * actual_changes[active_signals]
    
    # Statistics
    win_rate = np.sum(pnl > class="num">0) / len(pnl)
    avg_win = np.mean(pnl[pnl > class="num">0]) if np.any(pnl > class="num">0) else class="num">0
    avg_loss = np.mean(pnl[pnl < class="num">0]) if np.any(pnl < class="num">0) else class="num">0
    profit_factor = abs(np.sum(pnl[pnl > class="num">0]) / np.sum(pnl[pnl < class="num">0])) if np.sum(pnl[pnl < class="num">0]) != class="num">0 else class="type">class="kw">float(&class="macro">#x27;inf&class="macro">#x27;)
    
    result = {
        &class="macro">#x27;total_signals&class="macro">#x27;: len(pnl),
        &class="macro">#x27;win_rate&class="macro">#x27;: win_rate,
        &class="macro">#x27;avg_win&class="macro">#x27;: avg_win,
        &class="macro">#x27;avg_loss&class="macro">#x27;: avg_loss,
让小布替你算每根 K 线的夹角
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到实时角度序列与斜率分布,把重复劳动交给小布,你专注决策。外汇贵金属杠杆高、波动突兀,角度跳变往往先于肉眼可辨的破位。

常见问题

趋势线斜率是两点连线的粗略估计,连续角度是每个相邻价格点相对水平轴的精确数学值,能反映更细的节奏变化,可能比主观趋势线更早暴露拐点。
江恩体系按固定比例尺的纸质图设计,数字终端坐标自适应缩放,角度视觉随窗口变形;加之解读偏主观,不同人画出的结构可能完全相反。
可以,小布盯盘对应品种页已内置角度序列与机器学习标注模块,无需自己写 MQL5 指标即可看到斜率聚类与异常提示,省去重复造轮子。
模型对成千上万组连续夹角做聚类后,倾向于发现特定角度组合在行情启动前的概率偏移,这是人眼在嘈杂 K 线里很难稳定提取的微观旋律。
建议跨多个年份与至少两类品种(如欧元系与贵金属)分别跑,观察角度阈值在样本外是否仍具倾向性,避免把某段噪音当成普适规律。