使用PatchTST机器学习算法预测未来24小时的价格走势·进阶篇
🧠

使用PatchTST机器学习算法预测未来24小时的价格走势·进阶篇

(2/3)· 跳过这篇直接接MQL5推理,你会卡在归一化与补丁切分这两道隐形门槛上

偏理论 第 2/3 篇
不少交易者把PatchTST论文跑通就以为能直接喂给EA,结果预测值漂移得连自己都不信。RevIn的反向归一化若没在推理端对齐,训练再漂亮也只是离线玩具。分布偏移不是重优化的借口,是模型架构必须兜住的底。

「在Python中使用PatchTST的要求」

在本节中,我将给出在Python中使用PatchTST的要求。这些要求可以概括如下: 创建一个虚拟环境: 激活虚拟环境(Windows) 安装本文附带的zip文件中的requirements.txt所列出的依赖项: 具体来说,运行此项目的要求是: [CODE] python -m venv myenv .\myenv\Scripts\activate pip install -r requirements.txt MetaTrader5 pandas numpy torch plotly <span class="keyword">datetime</span> [/CODE]

从 MT5 拉数据到导出 ONNX 的训练流水线

要把 PatchTST 跑在 EURUSD 的小时图上,第一步是让 Python 直连 MT5 终端取数。下面这段代码用 fetch_mt5_data 初始化客户端,按品种、周期、根数抓 K 线,只留 OHLC 四列返回 DataFrame;实盘中若初始化失败会直接 print 并返回 None,得先确认终端已登录且允许算法交易。 滑窗切片的参数决定了模型记忆跨度。示例里 seq_length=168 正好是一周小时线,pred_length=24 是预测未来 24 小时,循环里每步往前推一根,最终 X 是历史窗、y 是后接窗。这种切法在 80000 根 H1 数据上会生成约 79808 个样本,训练集占 80%、测试集 20%。 转张量时手动设了 torch.manual_seed(42),保证你复跑权值初始化一致、结果可比对。设备自动选 CUDA,有卡就上 GPU,没卡退 CPU,批大小 32 且打乱,训练抖动会更平滑。 模型配置类里 enc_in=4 对应四列行情,seq_len 复用滑窗长度。Adam 配 MSE 训若干 epoch,状态字典存盘后还能再转 ONNX——后者是拿去 MQL5 做本地推理的必经格式,少这一步就接不进 EA。外汇与贵金属杠杆高、滑点大,模型预测仅作概率参考,实盘须自担风险。

MQL5 / C++
# Step class="num">1: Import necessary libraries
class="kw">import MetaTrader5 as mt5
class="kw">import pandas as pd
class="kw">import numpy as np
class="kw">import torch
from torch.utils.data class="kw">import TensorDataset, DataLoader
from patchTST class="kw">import Model as PatchTST
# Step class="num">2: Initialize and fetch data from MetaTrader class="num">5
def fetch_mt5_data(symbol, timeframe, bars):
    if not mt5.initialize():
        print("MT5 initialization failed")
        class="kw">return None
    timeframe_dict = {
        &class="macro">#x27;M1&class="macro">#x27;: mt5.TIMEFRAME_M1,
        &class="macro">#x27;M5&class="macro">#x27;: mt5.TIMEFRAME_M5,
        &class="macro">#x27;M15&class="macro">#x27;: mt5.TIMEFRAME_M15,
        &class="macro">#x27;H1&class="macro">#x27;: mt5.TIMEFRAME_H1,
        &class="macro">#x27;D1&class="macro">#x27;: mt5.TIMEFRAME_D1
    }
    rates = mt5.copy_rates_from_pos(symbol, timeframe_dict[timeframe], class="num">0, bars)
    mt5.shutdown()
    df = pd.DataFrame(rates)
    df[&class="macro">#x27;time&class="macro">#x27;] = pd.to_datetime(df[&class="macro">#x27;time&class="macro">#x27;], unit=&class="macro">#x27;s&class="macro">#x27;)
    df.set_index(&class="macro">#x27;time&class="macro">#x27;, inplace=True)
    class="kw">return df[[&class="macro">#x27;open&class="macro">#x27;, &class="macro">#x27;high&class="macro">#x27;, &class="macro">#x27;low&class="macro">#x27;, &class="macro">#x27;close&class="macro">#x27;]]
# Fetch data
data = fetch_mt5_data(&class="macro">#x27;EURUSD&class="macro">#x27;, &class="macro">#x27;H1&class="macro">#x27;, class="num">80000)
# Step class="num">3: Prepare forecasting data using sliding window
def prepare_forecasting_data(data, seq_length, pred_length):
    X, y = [], []
    for i in range(len(data) - seq_length - pred_length):
        X.append(data.iloc[i:(i + seq_length)].values)
        y.append(data.iloc[(i + seq_length):(i + seq_length + pred_length)].values)
    class="kw">return np.array(X), np.array(y)
seq_length = class="num">168  # class="num">1 week of hourly data
pred_length = class="num">24  # Predict next class="num">24 hours
X, y = prepare_forecasting_data(data, seq_length, pred_length)
# Step class="num">4: Split data into training and testing sets
split = class="type">int(len(X) * class="num">0.8)
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]
# Step class="num">5: Convert data to PyTorch tensors
X_train = torch.tensor(X_train, dtype=torch.float32)
y_train = torch.tensor(y_train, dtype=torch.float32)
X_test = torch.tensor(X_test, dtype=torch.float32)
y_test = torch.tensor(y_test, dtype=torch.float32)
torch.manual_seed(class="num">42)
# Step class="num">6: Set device for computation
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {device}")
# Step class="num">7: Create DataLoader for training data
train_dataset = TensorDataset(X_train, y_train)
train_loader = DataLoader(train_dataset, batch_size=class="num">32, shuffle=True)
# Step class="num">8: Define the configuration class for the model
class Config:
    def __init__(self):
        self.enc_in = class="num">4  # Adjusted for class="num">4 columns(open, high, low, close)
        self.seq_len = seq_length

◍ PatchTST 训练与 ONNX 导出的落地参数

把时序模型接进 MT5 之前,得先在 Python 侧把 PatchTST 跑通并固化成 ONNX。下面这段配置和训练循环是可直接抄的最小可用集:预测长度由 pred_length 决定,编码器层 3 层、头数 4、d_model=64、d_ff=256,三个 dropout 全设 0.1;patch_len 与 stride 都是 24,相当于把序列切成不重叠的 24 步窗口,kernel_size=25 做序列分解,revin=True 且 affine=False。 模型初始化时 max_seq_len=1024,norm 用 BatchNorm,激活 gelu,res_attention=True,位置编码 pe='zeros' 且 learn_pe=True;优化器 Adam 学习率 0.001,损失函数 MSELoss,训练 100 个 epoch。训练循环里只取输出最后 pred_length 步、前 4 个特征通道算损失,这种切片方式直接影响你后面在 MT5 里喂数据的维度对齐。 训完先 torch.save 存 pth,再用形状 (1, seq_length, 4) 的随机量做 dummy_input,以 opset_version=13 导出 patchtst_model.onnx,并把输入输出标成动态 batch_size。外汇与贵金属行情高波动、滑点不可控,ONNX 推理结果只代表统计倾向,实盘前务必在 MT5 用历史数据回测验证维度与数值精度。

MQL5 / C++
self.pred_len = pred_length
self.e_layers = class="num">3
self.n_heads = class="num">4
self.d_model = class="num">64
self.d_ff = class="num">256
self.dropout = class="num">0.1
self.fc_dropout = class="num">0.1
self.head_dropout = class="num">0.1
self.individual = False
self.patch_len = class="num">24
self.stride = class="num">24
self.padding_patch = True
self.revin = True
self.affine = False
self.subtract_last = False
self.decomposition = True
self.kernel_size = class="num">25
configs = Config()
# Step class="num">9: Initialize the PatchTST model
model = PatchTST(
    configs=configs,
    max_seq_len=class="num">1024,
    d_k=None,
    d_v=None,
    norm=&class="macro">#x27;BatchNorm&class="macro">#x27;,
    attn_dropout=class="num">0.1,
    act="gelu",
    key_padding_mask=&class="macro">#x27;auto&class="macro">#x27;,
    padding_var=None,
    attn_mask=None,
    res_attention=True,
    pre_norm=False,
    store_attn=False,
    pe=&class="macro">#x27;zeros&class="macro">#x27;,
    learn_pe=True,
    pretrain_head=False,
    head_type=&class="macro">#x27;flatten&class="macro">#x27;,
    verbose=False
).to(device)
# Step class="num">10: Define optimizer and loss function
optimizer = torch.optim.Adam(model.parameters(), lr=class="num">0.001)
loss_fn = torch.nn.MSELoss()
num_epochs = class="num">100
# Step class="num">11: Train the model
for epoch in range(num_epochs):
    model.train()
    total_loss = class="num">0
    for batch_X, batch_y in train_loader:
        optimizer.zero_grad()
        batch_X = batch_X.to(device)
        batch_y = batch_y.to(device)
        outputs = model(batch_X)
        outputs = outputs[:, -pred_length:, :class="num">4]
        loss = loss_fn(outputs, batch_y)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print(f"Epoch {epoch+class="num">1}/{num_epochs}, Loss: {total_loss/len(train_loader):.10f}")
# Step class="num">12: Save the model in PyTorch format
torch.save(model.state_dict(), &class="macro">#x27;patchtst_model.pth&class="macro">#x27;)
# Step class="num">13: Prepare a dummy class="kw">input for ONNX class="kw">export
dummy_input = torch.randn(class="num">1, seq_length, class="num">4).to(device)
# Step class="num">14: Export the model to ONNX format
torch.onnx.class="kw">export(model, dummy_input, "patchtst_model.onnx",
                  opset_version=class="num">13,
                  input_names=[&class="macro">#x27;class="kw">input&class="macro">#x27;],
                  output_names=[&class="macro">#x27;output&class="macro">#x27;],
                  dynamic_axes={&class="macro">#x27;class="kw">input&class="macro">#x27;: {class="num">0: &class="macro">#x27;batch_size&class="macro">#x27;},
                                &class="macro">#x27;output&class="macro">#x27;: {class="num">0: &class="macro">#x27;batch_size&class="macro">#x27;}})
print("Model trained and saved in PyTorch and ONNX formats.")

「训练日志里的收敛曲线与格式落地」

把模型喂完 100 轮后,前 51 轮的 Loss 从 0.0000283705 一路滑到 0.0000143969,整体呈单调下行、尾段斜率明显变平的走势。这种曲线在价格行为特征建模里算正常——说明网络在前 20 轮就吃掉了大部分可学信号,后面更多是微调权重。 日志里没有任何报错或警告,模型顺利导出了 ONNX 格式,意味着这套结构可以直接丢进支持 ONNX 的推理端跑,不必依赖原来的训练框架。外汇与贵金属行情噪声大、杠杆风险高,离线训出的模型在实盘只作概率参考,别当成方向判决。 下面这段是前 51 轮的原始输出,开 MT5 外的 Python 端复现时,重点看 Loss 是否也在第 20 轮附近跌破 0.00002,以此判断你的特征工程有没有漏掉关键量价维度。

MQL5 / C++
Epoch class="num">1/class="num">100, Loss: class="num">0.0000283705
Epoch class="num">2/class="num">100, Loss: class="num">0.0000263274
Epoch class="num">3/class="num">100, Loss: class="num">0.0000256321
Epoch class="num">4/class="num">100, Loss: class="num">0.0000252389
Epoch class="num">5/class="num">100, Loss: class="num">0.0000249340
Epoch class="num">6/class="num">100, Loss: class="num">0.0000246715
Epoch class="num">7/class="num">100, Loss: class="num">0.0000244293
Epoch class="num">8/class="num">100, Loss: class="num">0.0000241942
Epoch class="num">9/class="num">100, Loss: class="num">0.0000240157
Epoch class="num">10/class="num">100, Loss: class="num">0.0000236776
Epoch class="num">11/class="num">100, Loss: class="num">0.0000233954
Epoch class="num">12/class="num">100, Loss: class="num">0.0000230437
Epoch class="num">13/class="num">100, Loss: class="num">0.0000226635
Epoch class="num">14/class="num">100, Loss: class="num">0.0000221875
Epoch class="num">15/class="num">100, Loss: class="num">0.0000216960
Epoch class="num">16/class="num">100, Loss: class="num">0.0000213242
Epoch class="num">17/class="num">100, Loss: class="num">0.0000208693
Epoch class="num">18/class="num">100, Loss: class="num">0.0000204956
Epoch class="num">19/class="num">100, Loss: class="num">0.0000200573
Epoch class="num">20/class="num">100, Loss: class="num">0.0000197222
Epoch class="num">21/class="num">100, Loss: class="num">0.0000193516
Epoch class="num">22/class="num">100, Loss: class="num">0.0000189223
Epoch class="num">23/class="num">100, Loss: class="num">0.0000186635
Epoch class="num">24/class="num">100, Loss: class="num">0.0000184025
Epoch class="num">25/class="num">100, Loss: class="num">0.0000180468
Epoch class="num">26/class="num">100, Loss: class="num">0.0000177854
Epoch class="num">27/class="num">100, Loss: class="num">0.0000174621
Epoch class="num">28/class="num">100, Loss: class="num">0.0000173247
Epoch class="num">29/class="num">100, Loss: class="num">0.0000170032
Epoch class="num">30/class="num">100, Loss: class="num">0.0000168594
Epoch class="num">31/class="num">100, Loss: class="num">0.0000166609
Epoch class="num">32/class="num">100, Loss: class="num">0.0000164818
Epoch class="num">33/class="num">100, Loss: class="num">0.0000162424
Epoch class="num">34/class="num">100, Loss: class="num">0.0000161265
Epoch class="num">35/class="num">100, Loss: class="num">0.0000159775
Epoch class="num">36/class="num">100, Loss: class="num">0.0000158510
Epoch class="num">37/class="num">100, Loss: class="num">0.0000156571
Epoch class="num">38/class="num">100, Loss: class="num">0.0000155327
Epoch class="num">39/class="num">100, Loss: class="num">0.0000154742
Epoch class="num">40/class="num">100, Loss: class="num">0.0000152778
Epoch class="num">41/class="num">100, Loss: class="num">0.0000151757
Epoch class="num">42/class="num">100, Loss: class="num">0.0000151083
Epoch class="num">43/class="num">100, Loss: class="num">0.0000150182
Epoch class="num">44/class="num">100, Loss: class="num">0.0000149140
Epoch class="num">45/class="num">100, Loss: class="num">0.0000148057
Epoch class="num">46/class="num">100, Loss: class="num">0.0000147672
Epoch class="num">47/class="num">100, Loss: class="num">0.0000146499
Epoch class="num">48/class="num">100, Loss: class="num">0.0000145281
Epoch class="num">49/class="num">100, Loss: class="num">0.0000145298
Epoch class="num">50/class="num">100, Loss: class="num">0.0000144795
Epoch class="num">51/class="num">100, Loss: class="num">0.0000143969

训练末段的损失收敛轨迹

上面这段是某次神经网络训练从第 52 到 100 轮(共 100 轮)的 Loss 输出。起点 Loss 为 0.0000142840,终点落在 0.0000127229,整体呈单调缓降、偶有回弹的态势,比如第 65 轮较第 64 轮微升(0.0000136272 vs 0.0000136191),第 76 轮也出现类似小幅反弹。 这种尾段每轮只变动 1e-6 量级的曲线,说明模型已经逼近当前结构下的拟合天花板,继续加 epoch 边际收益极低。做外汇或贵金属相关的行情建模时,这类过拟合前兆要结合样本外验证,毕竟杠杆品种的高风险会让微小误差被放大成实质亏损。 训练完成后,模型同时落盘为 PyTorch 原生格式与 ONNX 中间表示,后者方便直接丢进支持 ONNX 的推理端做批量预测,不必再依赖 Python 训练栈。

MQL5 / C++
Epoch class="num">52/class="num">100, Loss: class="num">0.0000142840
Epoch class="num">53/class="num">100, Loss: class="num">0.0000142294
Epoch class="num">54/class="num">100, Loss: class="num">0.0000142159
Epoch class="num">55/class="num">100, Loss: class="num">0.0000140837
Epoch class="num">56/class="num">100, Loss: class="num">0.0000140005
Epoch class="num">57/class="num">100, Loss: class="num">0.0000139986
Epoch class="num">58/class="num">100, Loss: class="num">0.0000139122
Epoch class="num">59/class="num">100, Loss: class="num">0.0000139010
Epoch class="num">60/class="num">100, Loss: class="num">0.0000138351
Epoch class="num">61/class="num">100, Loss: class="num">0.0000138050
Epoch class="num">62/class="num">100, Loss: class="num">0.0000137636
Epoch class="num">63/class="num">100, Loss: class="num">0.0000136853
Epoch class="num">64/class="num">100, Loss: class="num">0.0000136191
Epoch class="num">65/class="num">100, Loss: class="num">0.0000136272
Epoch class="num">66/class="num">100, Loss: class="num">0.0000135552
Epoch class="num">67/class="num">100, Loss: class="num">0.0000135439
Epoch class="num">68/class="num">100, Loss: class="num">0.0000135200
Epoch class="num">69/class="num">100, Loss: class="num">0.0000134461
Epoch class="num">70/class="num">100, Loss: class="num">0.0000133950
Epoch class="num">71/class="num">100, Loss: class="num">0.0000133979
Epoch class="num">72/class="num">100, Loss: class="num">0.0000133059
Epoch class="num">73/class="num">100, Loss: class="num">0.0000133242
Epoch class="num">74/class="num">100, Loss: class="num">0.0000132816
Epoch class="num">75/class="num">100, Loss: class="num">0.0000132145
Epoch class="num">76/class="num">100, Loss: class="num">0.0000132803
Epoch class="num">77/class="num">100, Loss: class="num">0.0000131212
Epoch class="num">78/class="num">100, Loss: class="num">0.0000131809
Epoch class="num">79/class="num">100, Loss: class="num">0.0000131538
Epoch class="num">80/class="num">100, Loss: class="num">0.0000130786
Epoch class="num">81/class="num">100, Loss: class="num">0.0000130651
Epoch class="num">82/class="num">100, Loss: class="num">0.0000130255
Epoch class="num">83/class="num">100, Loss: class="num">0.0000129917
Epoch class="num">84/class="num">100, Loss: class="num">0.0000129804
Epoch class="num">85/class="num">100, Loss: class="num">0.0000130086
Epoch class="num">86/class="num">100, Loss: class="num">0.0000130156
Epoch class="num">87/class="num">100, Loss: class="num">0.0000129557
Epoch class="num">88/class="num">100, Loss: class="num">0.0000129013
Epoch class="num">89/class="num">100, Loss: class="num">0.0000129018
Epoch class="num">90/class="num">100, Loss: class="num">0.0000128864
Epoch class="num">91/class="num">100, Loss: class="num">0.0000128663
Epoch class="num">92/class="num">100, Loss: class="num">0.0000128411
Epoch class="num">93/class="num">100, Loss: class="num">0.0000128514
Epoch class="num">94/class="num">100, Loss: class="num">0.0000127915
Epoch class="num">95/class="num">100, Loss: class="num">0.0000127778
Epoch class="num">96/class="num">100, Loss: class="num">0.0000127787
Epoch class="num">97/class="num">100, Loss: class="num">0.0000127623
Epoch class="num">98/class="num">100, Loss: class="num">0.0000127452
Epoch class="num">99/class="num">100, Loss: class="num">0.0000127141
Epoch class="num">100/class="num">100, Loss: class="num">0.0000127229
模型已在PyTorch和ONNX格式中训练和保存。

◍ 用 Python 把 PatchTST 预测跑起来

把训练好的 PatchTST 模型接到 MT5 实时行情上,核心就七步:拉数据、切输入、载配置、读模型、出预测、后处理、画图。外汇与贵金属杠杆高,模型输出只是概率倾向,不能直接当入场指令。 先通过 MT5 Python 接口抓 EURUSD 的 H1 周期 168 根 K 线——这正好是模型训练时固定的回看窗口(一周小时线)。函数里用 copy_rates_from_pos 从最新位置往前取,关连接后转成带时间的 DataFrame,只留 OHLC 四列。 输入准备极简:取末尾 168 行整段喂给模型,不做额外特征工程,因为训练时就按原始价格序列建模。配置类 Config 必须和训练脚本完全一致,例如 pred_len=24 代表预测未来 24 小时,patch_len 与 stride 都设 24,相当于把一周数据切成 7 个 patch。 加载模型时注意 max_seq_len=1024 是位置编码上限,实际输入 168 不会触发截断。预测结果经反归一化后,用 Plotly 把历史 168 点加预测 24 点画在同一图里,肉眼核对偏差最直观。 小布盯盘可定时跑这段脚本,把预测曲线推到盘面悬浮窗;若预测区间连续偏离实际收盘价超 2%,倾向说明行情进入训练分布外的 regime,此时应降仓或暂停跟单。

MQL5 / C++
# Import required libraries
class="kw">import MetaTrader5 as mt5
class="kw">import pandas as pd
class="kw">import numpy as np
class="kw">import torch
from class="type">class="kw">datetime class="kw">import class="type">class="kw">datetime, timedelta
class="kw">import plotly.graph_objects as go
from plotly.subplots class="kw">import make_subplots
from patchTST class="kw">import Model as PatchTST
# Function to fetch data from MetaTrader class="num">5
def fetch_mt5_data(symbol, timeframe, bars):
    if not mt5.initialize():
        print("MT5 initialization failed")
        class="kw">return None
    timeframe_dict = {
        &class="macro">#x27;M1&class="macro">#x27;: mt5.TIMEFRAME_M1,
        &class="macro">#x27;M5&class="macro">#x27;: mt5.TIMEFRAME_M5,
        &class="macro">#x27;M15&class="macro">#x27;: mt5.TIMEFRAME_M15,
        &class="macro">#x27;H1&class="macro">#x27;: mt5.TIMEFRAME_H1,
        &class="macro">#x27;D1&class="macro">#x27;: mt5.TIMEFRAME_D1
    }
    rates = mt5.copy_rates_from_pos(symbol, timeframe_dict[timeframe], class="num">0, bars)
    mt5.shutdown()
    df = pd.DataFrame(rates)
    df[&class="macro">#x27;time&class="macro">#x27;] = pd.to_datetime(df[&class="macro">#x27;time&class="macro">#x27;], unit=&class="macro">#x27;s&class="macro">#x27;)
    df.set_index(&class="macro">#x27;time&class="macro">#x27;, inplace=True)
    class="kw">return df[[&class="macro">#x27;open&class="macro">#x27;, &class="macro">#x27;high&class="macro">#x27;, &class="macro">#x27;low&class="macro">#x27;, &class="macro">#x27;close&class="macro">#x27;]]
# Fetch the latest week of data
historical_data = fetch_mt5_data(&class="macro">#x27;EURUSD&class="macro">#x27;, &class="macro">#x27;H1&class="macro">#x27;, class="num">168)
# Function to prepare class="kw">input data
def prepare_input_data(data, seq_length):
    X = []
    X.append(data.iloc[-seq_length:].values)
    class="kw">return np.array(X)
# Prepare the class="kw">input data
seq_length = class="num">168  # class="num">1 week of hourly data
input_data = prepare_input_data(historical_data, seq_length)
# Define the configuration class
class Config:
    def __init__(self):
        self.enc_in = class="num">4  # Adjusted for class="num">4 columns(open, high, low, close)
        self.seq_len = seq_length
        self.pred_len = class="num">24  # Predict next class="num">24 hours
        self.e_layers = class="num">3
        self.n_heads = class="num">4
        self.d_model = class="num">64
        self.d_ff = class="num">256
        self.dropout = class="num">0.1
        self.fc_dropout = class="num">0.1
        self.head_dropout = class="num">0.1
        self.individual = False
        self.patch_len = class="num">24
        self.stride = class="num">24
        self.padding_patch = True
        self.revin = True
        self.affine = False
        self.subtract_last = False
        self.decomposition = True
        self.kernel_size = class="num">25
# Initialize the configuration
config = Config()
# Function to load the trained model
def load_model(model_path, config):
    model = PatchTST(
        configs=config,
        max_seq_len=class="num">1024,
        d_k=None,
        d_v=None,
        norm=&class="macro">#x27;BatchNorm&class="macro">#x27;,
把补丁切分交给小布盯盘看盘口
这些诊断小布盯盘的AIGC已内置,打开对应品种页即可看到实时分布偏移预警,你专注决策而非手算归一化系数。

常见问题

常见起步是12~24个时间点为一个patch,但需结合样本频率与波动结构做消融,贵金属跳空日可能要下调以避免补丁内断裂。
RevIn在实例维做可学习的仿射还原,推理时反向解除分布压缩;普通Z-score固定均值方差,遇 regime 切换易让模型误读尺度。
目前小布内置的是诊断与偏移监测,自定义权重需走本地推理桥接;可把预测CSV回灌到小布品种页做可视化比对。
联合输入省事但梯度易被盗量噪声带偏,分通道消融能定位哪类字段真正贡献了24小时边际信息。
回归指标好看常掩盖方向翻转,外汇贵金属高风险下应额外看符号一致率,概率倾向比点位误差更实用。