一次基于 FMZ Rust 的在线学习对照实验
在线学习在量化研究里很容易被包装成一项天然优势:市场是非平稳的,所以模型应该持续吸收新数据。
这个推理只走了一半。
模型更新得更快,确实可能更早摆脱已经失效的历史关系;但同一套更新机制,也会更快地把短期随机波动写进参数。对交易策略来说,后者不仅表现为预测不稳定,还会转化为更高的换手、更强的成本敏感性,以及更大的路径依赖。
因此,本文不试图证明“在线模型优于固定模型”。实验要回答的是一个更具体的问题:
当模型结构、特征、交易规则和成本假设完全相同时,改变模型吸收新样本的速度,会怎样影响样本外损失、参数稳定性、换手和虚拟净值?
为避免把模型差异和回测环境差异混在一起,四套模型在同一个 Rust 策略进程内并行运行,读取同一组 K 线,使用相同的特征和虚拟成交规则。策略不向交易所发单,只维护四个影子账户。
本文保留一组实际回测,用来检查时间对齐、标签分布、预测损失、换手和交易成本是否符合预期。但单次回测不会被当作普遍结论。策略的主要价值,仍然是建立一套可复现的比较框架。
1. 研究问题:更新速度本身就是模型风险
在线学习通常采用“先预测、后观察标签、再更新”的顺序。第 \(t\) 个样本到来时,模型先用当前参数做预测;真实结果出现后,计算损失并更新参数。
这类方法的优势是无需反复保存和重训全部历史数据,也能逐步吸收新信息。问题在于,每条新样本都会立即影响参数。
可以把更新机制理解为一条连续的速度轴:
text
永不更新
── 定期滚动重训
── 误差恶化后更新
── 每根 K 线更新
── 每个 Tick 更新
越靠左,参数越稳定,但面对结构变化时反应更慢;越靠右,模型越灵活,也越容易被短期噪声推动。
因此,本次实验预先提出三个待检验的判断,而不是预设答案:
- 逐根在线更新可能缩短模型在行情切换后的恢复时间;
- 同一机制也可能放大参数波动、信号翻转和交易成本;
- 模型表现未必随更新频率单调改善,中间速度可能更稳。
2. 实验设计:只改变模型怎样吸收新样本
四组模型共享同一套初始化参数。
| 模型 | 更新方式 | 研究含义 |
|---|---|---|
| 固定模型 | 初始化后不再更新 | 观察历史关系自然衰减的速度 |
| 逐根在线 | 每获得一个新标签就做一次梯度更新 | 最快的样本吸收速度 |
| 滚动重训 | 每隔固定根数,用最近窗口从头训练 | 降低单个样本的即时影响 |
| 误差门控 | 近期 Log Loss 明显恶化后,用小批近期样本低速更新 | 在稳定和适应之间加一道门槛 |
第四组只能称为“误差门控更新”,不能称为严格的概念漂移检测器。
预测损失上升可能来自条件分布变化,也可能只是波动率上升、异常值、标签随机性或模型本身校准不佳。本文只利用损失恶化作为工程触发条件,不根据它断言市场已经发生概念漂移。
为了保证比较尽量干净,四组模型使用完全相同的:
- 初始训练样本;
- 特征及标准化参数;
- 逻辑回归结构;
- 多空概率阈值;
- 收益计算方式;
- 单边换手成本假设。
四个虚拟账户也在同一根 K 线上同步结算。这样,结果差异主要来自更新机制,而不是数据切片、接口时点或回测撮合差异。
3. 时间对齐:先把未来函数和成交假设处理清楚
在线模型最容易出错的地方不是梯度公式,而是样本时间。
本文在第 \(t\) 根 K 线收盘后计算特征 \(x_t\),预测第 \(t+1\) 根 K 线从开盘到收盘的方向:

这里的 0 类准确地说是“非上涨”,因为开盘价等于收盘价时也会被编码为 0。
标签函数如下:
rust
fn make_label(
bars: &[Bar],
feature_index: usize,
) -> f64 {
let next_bar =
bars[feature_index + 1];
if next_bar.close > next_bar.open {
1.0
} else {
0.0
}
}
流程如下:
text
第 t 根 K 线结束
↓
计算 x_t 并预测第 t+1 根方向
↓
假定在第 t+1 根开盘附近建立仓位
↓
第 t+1 根收盘,得到标签和开盘到收盘收益
↓
先评价原预测,再允许模型更新
这种定义没有假定策略能够在已经知道收盘价后,仍成交在第 \(t\) 根 K 线的收盘价上。
虚拟账户使用第 \(t+1\) 根 K 线的开盘到收盘收益:

这仍然是简化模型。真实订单未必能成交在下一根 K 线的开盘价,盘口冲击、延迟和滑点也不会固定。代码中的成本只是统一的压力测试假设,不代表任何交易所的真实费率。
3.1 回测与实盘分别判断完整 K 线
第一版策略曾统一使用当前时间判断 K 线是否结束。在实际回测中,这种写法把正在形成的 Bar 提前当成了完整样本,导致大量 K 线出现 Open == Close,模型准确率异常接近 100%。
修正版通过 IsVirtual() 区分运行环境:
- 回测环境中,保守使用行情数组的倒数第二根 Bar;
- 实盘环境中,检查最后一根 Bar 的起始时间加周期长度是否已经不晚于当前时间;
- 如果实盘接口中的最后一根仍未结束,则退回倒数第二根。
rust
fn latest_closed_index(
bars: &[Bar],
is_backtest: bool,
bar_seconds: i64,
) -> Option<usize> {
if bars.len() < 2 {
return None;
}
if is_backtest {
return Some(bars.len() - 2);
}
let last_index = bars.len() - 1;
let period_ms = bar_seconds * 1000;
let current_time_ms =
(UnixNano() / 1_000_000) as i64;
if bars[last_index].time + period_ms
<= current_time_ms
{
Some(last_index)
} else {
Some(last_index - 1)
}
}
少处理一根 K 线的代价,远小于把未完成数据误作标签。
3.2 Test-then-train
每个新标签出现时,处理顺序严格保持为:
- 使用之前保存的预测计算准确率和 Log Loss;
- 使用之前确定的仓位计算当前 K 线收益;
- 将当前样本加入训练数据;
- 按各模型的规则更新参数;
- 使用当前完整 K 线生成下一根预测。
也就是先测试,再训练。当前样本不会先进入模型,再拿同一条样本评价模型。
4. 模型:简单的逻辑回归足够完成这次实验
本文没有使用神经网络或树模型,而是使用带 L2 正则的逻辑回归:

其中 \(p_t\) 是下一根 K 线上涨的估计概率。
逐根更新采用二元交叉熵对应的随机梯度:

上式只写出了权重项。代码中的偏置 \(b\) 也会同步更新,但不施加 L2 正则。
rust
fn predict(
&self,
features: &[f64; N_FEATURES],
) -> f64 {
let mut linear_value = self.bias;
for j in 0..N_FEATURES {
linear_value +=
self.weights[j] * features[j];
}
Self::sigmoid(linear_value)
}
fn update(
&mut self,
features: &[f64; N_FEATURES],
label: f64,
learning_rate_scale: f64,
) {
let probability = self.predict(features);
let error = probability - label;
let step =
self.learning_rate
* learning_rate_scale;
for j in 0..N_FEATURES {
let gradient = clip(
error * features[j]
+ self.l2_penalty
* self.weights[j],
-5.0,
5.0,
);
self.weights[j] -= step * gradient;
}
self.bias -=
step * clip(error, -1.0, 1.0);
self.updates += 1;
}
选择简单模型并不是认为逻辑回归足以描述市场,而是为了减少实验中的混杂因素。
如果同时改变网络结构、训练轮数、特征数量和更新频率,最后很难判断收益差异来自哪里。线性模型的参数变化可以直接测量,也便于观察“模型适应”是否伴随着过大的权重漂移。
代码还对单次梯度和标准化特征做了截断,目的不是提高收益,而是避免极端 K 线让参数在一次更新中跳得过远。
5. 特征:只保留价格结构和成交量的基础信息
模型使用八个特征:
- 单根对数收益;
- K 线实体的对数收益;
- 最高价与最低价的对数区间;
- 上影线相对长度;
- 下影线相对长度;
- 成交量对数变化;
- 三根 K 线动量;
- 最近五根 K 线收益的实现波动率。
没有加入 RSI、MACD、均线交叉等派生指标,也没有使用几十个滞后项。
下面是特征计算中的主要部分:
rust
fn raw_features(
bars: &[Bar],
index: usize,
) -> [f64; N_FEATURES] {
let current = bars[index];
let previous = bars[index - 1];
let epsilon = 1e-12;
let log_return_1 =
(current.close.max(epsilon)
/ previous.close.max(epsilon))
.ln();
let candle_body =
(current.close.max(epsilon)
/ current.open.max(epsilon))
.ln();
let range =
(current.high.max(epsilon)
/ current.low.max(epsilon))
.ln();
let upper_wick =
(current.high
- current.open.max(current.close))
.max(0.0)
/ current.open.max(epsilon);
let lower_wick =
(current.open.min(current.close)
- current.low)
.max(0.0)
/ current.open.max(epsilon);
let volume_change =
((current.volume + epsilon)
/ (previous.volume + epsilon))
.ln();
let momentum_3 =
(current.close.max(epsilon)
/ bars[index - 3]
.close
.max(epsilon))
.ln();
// 最近五根收益的实现波动率
// 在完整代码中按同一窗口继续计算。
[
log_return_1,
candle_body,
range,
upper_wick,
lower_wick,
volume_change,
momentum_3,
realized_volatility,
]
}
初始样本被按时间分为前 80% 和后 20%。标准化器只用前 80% 的特征拟合均值和标准差,之后保持固定。四套模型始终使用同一个标准化器。
这里特意不让标准化器在线更新。否则,逐根模型获得的不仅是参数更新,还多了一层特征分布自适应,比较对象就不再单一。
固定标准化器有利于保持四组模型可比,但当特征分布长期变化时,也可能产生尺度失配。因此,本次结果同时包含模型关系失效和固定标准化尺度失配的影响,二者并没有被单独识别。
6. 初始化:四个模型必须站在同一条起跑线上
代码默认准备 320 条初始监督样本。
初始化分两步:
- 使用前 80% 样本做六轮批量训练;
- 对后 20% 样本执行逐条的“预测—记录损失—更新”。
第二段得到的平均预测前 Log Loss,被用作误差门控模型的参考损失。处理完这段样本后得到的模型,才是四组模型共同的初始参数。
rust
fn fit_initial_model(
samples: &[Sample],
split_index: usize,
config: &Config,
) -> (LogisticModel, f64) {
let mut model =
train_model(
&samples[..split_index],
config.initial_epochs,
config,
);
let mut loss_sum = 0.0;
let mut count = 0usize;
for sample in &samples[split_index..] {
let probability =
model.predict(&sample.x);
loss_sum +=
log_loss(
probability,
sample.y,
);
count += 1;
model.update(
&sample.x,
sample.y,
1.0,
);
}
let baseline_loss =
if count == 0 {
0.69314718056
} else {
loss_sum / count as f64
};
(model, baseline_loss)
}
这种处理解决了两个问题。
第一,参考损失不是模型在已经训练过的数据上的回看损失,而是按时间顺序得到的预测前损失。
第二,四组模型都从同一个、已经吸收全部初始化样本的参数状态开始。不会出现固定模型和在线模型的初始训练数据不同,却把后续差异归因于更新方式的情况。
7. 四种更新机制
7.1 固定模型
固定模型在初始化后不再修改权重。
它提供的是基准:如果后续表现持续恶化,说明初始化阶段学到的关系无法稳定外推;如果它反而最稳,则说明额外更新没有带来足够的信息增量。
需要注意,固定模型只表示权重和偏置不再更新,不代表预测概率或仓位保持不变。输入特征每根 K 线都在变化,固定参数同样可能产生频繁换手。
7.2 逐根在线模型
每个标签出现后执行一次梯度更新:
rust
online_model.update(
&previous_features,
label,
1.0,
);
它对新环境反应最快,也最容易把连续几条偶然样本解释成结构变化。
学习率是这组模型最重要的风险参数。学习率越大,新样本对参数的影响越强。本文使用界面默认值 0.02,不做样本内寻优。
7.3 滚动重训模型
在默认参数下,滚动模型保存最近 240 条样本,每经过 32 条新样本,将模型参数清零,再用窗口内样本重新训练。
rust
if processed_bars
% config.retrain_every
== 0
{
periodic_model =
retrain_from_window(
&rolling_samples,
&config,
);
periodic_retrains += 1;
}
这种方式有两个特征:
- 旧样本会被明确移出训练集;
- 单根新样本不会立即改变模型,而是等到下一次重训时与一批近期样本共同起作用。
它的适应有延迟,但参数变化通常不会像逐根更新那样完全由最后一条样本推动。滚动窗口和重训间隔都可以在策略界面中调整。
7.4 误差门控模型
门控模型维护自身预测 Log Loss 的指数移动平均:

当近期损失高于初始化参考损失的一定比例,且不处于冷却期时,模型才使用最近一小批样本进行低学习率更新。
rust
let current_gated_loss =
log_loss(
gated_old_probability,
label,
);
gated_loss_ema =
(1.0 - config.loss_ema_alpha)
* gated_loss_ema
+ config.loss_ema_alpha
* current_gated_loss;
let gate_threshold =
baseline_loss
* config.gate_loss_multiplier;
if gate_cooldown_left == 0
&& gated_loss_ema > gate_threshold
{
let batch_start =
rolling_samples
.len()
.saturating_sub(
config.gate_batch
);
for (sample_index, sample)
in rolling_samples
.iter()
.enumerate()
{
if sample_index >= batch_start {
gated_model.update(
&sample.x,
sample.y,
config.gated_lr_scale,
);
}
}
gate_events += 1;
gate_cooldown_left =
config.gate_cooldown;
}
界面默认参数为:
- 损失阈值:参考损失的 1.10 倍;
- 更新批量:最近 32 条样本;
- 学习率缩放:基础学习率的 0.25;
- 更新后冷却:32 根 K 线。
这些值只是未经优化的实验默认值。它们的作用是定义一套可重复的门控规则,不代表统计意义上的最优阈值。
8. 交易映射与成本
模型概率被映射为三种仓位:
rust
fn position_from_probability(
probability: f64,
config: &Config,
) -> i32 {
if probability >= config.long_threshold {
1
} else if probability
<= config.short_threshold
{
-1
} else {
0
}
}
默认规则是:
text
p >= 0.55:多仓
p <= 0.45:空仓
其他:不持仓
0.45 到 0.55 的中性区间用于减少模型在 0.5 附近的小幅波动直接转化为交易。
仓位变化量定义为:

因此:
- 空仓到多仓,换手为 1;
- 多仓到空仓,换手为 1;
- 多仓直接反为空仓,换手为 2。
成本已经设置为界面参数,而不是写死的代码常量:
rust
let cost_bps = float_parameter(
"CostBps",
CostBps,
0.0,
100.0,
true,
)?;
cost_per_turnover:
cost_bps / 10_000.0,
默认将 CostBps 设置为 5,即每单位单边换手扣除 5 个基点。该值是实验参数,可以在策略界面中调整,不代表具体交易所的实际费率。
9. 评价指标:不要只看准确率
四组模型同时记录以下指标:
| 指标 | 观察目的 |
|---|---|
| 平均 Log Loss | 概率预测是否变得更可靠,而不只是方向碰巧正确 |
| 方向准确率 | 0.5 阈值下的基本分类表现 |
| 虚拟净收益 | 预测能否在统一交易规则下转化为收益 |
| 最大回撤 | 更新机制是否增加净值路径风险 |
| 持仓率 | 某模型是否只是因为更少交易而显得稳定 |
| 累计换手 | 交易成本敏感性 |
| 仓位变化次数 | 信号是否频繁改口 |
| 多空直接翻转次数 | 模型是否在两个方向间剧烈摆动 |
| 参数位移 | 当前权重与共同初始模型之间的距离 |
其中,参数位移定义为权重和偏置差值的欧氏距离。由于特征使用同一个固定标准化器,四组模型之间可以做相对比较。
参数位移大本身不是错误。行情结构真的改变时,模型需要移动。需要警惕的是下面这种组合:
text
参数位移快速上升
+ 换手明显增加
+ Log Loss 没有改善
+ 扣成本净值恶化
这更像是模型在追逐近期样本,而不是获得了稳定的适应能力。
10. 默认参数下的一组实际回测
修正完整 K 线判断后,使用以下条件完成了一次基准回测:
text
品种:ETH_USDT.swap
周期:15 分钟
回测区间:2025-07-09 至 2026-06-21
初始化样本:320
样本外 K 线:33333
基础学习率:0.02
滚动窗口:240
单边换手成本:5 bps


样本外统计中,上涨标签为 16644 条,非上涨标签为 16689 条,其中有 36 根 K 线开盘价等于收盘价。状态栏当前显示为“下跌”,但代码中的实际口径是 Close <= Open,因此下文统一称为“非上涨”。
| 模型 | 准确率 | Log Loss | 持仓率 | 累计换手 | 多空翻转 | 参数位移 | 净收益 |
|---|---|---|---|---|---|---|---|
| 固定模型 | 52.07% | 0.7094 | 65.2% | 24755 | 4985 | 0.0000 | -100.00% |
| 逐根在线 | 51.44% | 0.7203 | 56.7% | 21073 | 3619 | 0.4409 | -100.00% |
| 滚动重训 | 51.74% | 0.7301 | 64.6% | 21271 | 4111 | 0.5712 | -100.00% |
| 误差门控 | 49.40% | 0.6986 | 18.3% | 6190 | 142 | 0.2989 | -98.00% |
-
10.1 时间对齐问题已经修正
上涨与非上涨标签基本均衡,模型准确率回到 49%~52%,初始化参考 Log Loss 为 0.6988。此前接近 100% 的准确率,确实来自未完成 K 线被提前处理,而不是模型突然获得了极强的预测能力。
-
10.2 更新更勤,没有改善样本外概率预测
对于始终输出 0.5 的朴素概率模型,每条样本的 Log Loss 都约为 0.6931。
本次回测中,四组模型的平均 Log Loss 均高于这一基准。误差门控最接近 0.6931,但仍不能认为模型已经获得有效预测能力。
逐根在线和滚动重训都产生了明显的参数位移,但 Log Loss 分别上升到 0.7203 和 0.7301,弱于固定模型。至少在当前品种、周期、特征和默认参数下,更频繁地更新模型没有改善样本外概率预测。
-
10.3 门控机制降低了噪声反应,但没有创造交易优势
误差门控模型只触发了 8 次更新。它的累计换手降到 6190,多空直接翻转只有 142 次,明显低于另外三组。
这说明门控机制至少完成了它的工程目标:减少模型因为短期误差而频繁改口。
但其成本后净值仍下降约 98%。准确的结论不是“误差门控已经有效”,而是:
受控更新比无条件更新更稳定,但稳定本身还没有转化为交易优势。
状态栏最终显示“未达到更新阈值”,只表示回测结束时损失 EMA 位于阈值下方,并不与历史上曾触发 8 次更新矛盾。
-
10.4 固定模型不更新参数,仍然可能频繁换手
固定模型的参数位移为 0,但累计换手反而是四组中最高的。
这并不矛盾。固定模型只表示权重和偏置不再变化,输入特征 \(x_t\) 每根 K 线仍然不同。参数固定、输入变化,预测概率和仓位依然会变化。
-
10.5 净值接近零后,收益指标已经饱和
四组模型在 5 bps 单边成本下都出现了严重亏损。主要问题不是某一次大额损失,而是在三万多根 K 线上累计了大量换手。
当净值接近零以后,净收益和最大回撤都接近极限值,已经很难继续区分模型之间的经济差异。
后续版本应至少增加:
- 扣成本前的毛收益;
- 累计成本;
- 净对数收益;
- 每千根 K 线换手;
- 0、2、5、10 bps 多种成本情景。
这组结果不能证明在线学习在其他市场中无效。它只能说明:在本次实验条件下,模型更新得更勤,并没有表现出更强的市场适应能力;受控更新减少了噪声反应,但稳定性还没有转化为交易优势。
11. 完整 Rust 实现
完整策略只读取 K 线,在内存中维护四个模型和四个虚拟账户,不包含真实下单逻辑。
12. 怎样判断模型是在适应,还是在追噪声
一次回测中在线模型净值最高,不能直接证明在线更新有效。更有信息量的是指标之间的关系,以及结论能否跨区间重复。
-
情形一:Log Loss 改善,换手没有明显上升
如果逐根模型在行情切换后较快降低 Log Loss,同时参数位移趋于稳定,累计换手与固定模型差距不大,这才是较强的适应证据。
这里的重点不是“参数动了”,而是参数移动后预测质量得到持续改善。
-
情形二:准确率提高,但 Log Loss 和净值变差
准确率只关心概率是否跨过 0.5,不关心模型有多自信。
模型可能把原来的 0.51 提高到 0.90,方向仍然正确;一旦判断错误,Log Loss 会显著恶化。交易上,这类过度自信也更容易让信号跨过多空阈值。
因此,在线模型准确率略高,但 Log Loss、回撤和成本后收益更差,并不矛盾。
-
情形三:零成本有优势,加入成本后消失
这通常意味着在线更新产生了更多边际信号,但单次优势不足以覆盖交易摩擦。
此时模型也许确实捕捉到了一点短期可预测性,但这种可预测性没有达到可交易强度。不能把毛收益提升等同于策略有效。
-
情形四:滚动重训或门控更新更稳
如果这两组在多个品种和周期上都表现出较低 Log Loss、较小回撤和较低换手,说明有用的适应速度可能位于“永不更新”和“逐根更新”之间。
仍需注意,滚动窗口、重训间隔和门控阈值本身也是超参数。不能只保留表现最好的一组设置。
-
情形五:固定模型最好
这并不说明市场是平稳的,只说明在当前特征、模型和样本区间中,新样本没有提供足够稳定的信息,或者在线更新规则没有正确提取这些信息。
固定模型参数稳定,并不代表信号更少。本次回测中,固定模型的累计换手反而是四组中最高的。
13. 结果应怎样记录
至少应按品种、周期和时间区间分别保存下表,而不是只保留一条总净值:
| 品种 | 周期 | 区间 | 模型 | Log Loss | 准确率 | 毛收益 | 累计成本 | 净收益 | 最大回撤 | 持仓率 | 换手 | 参数位移 |
|---|
还应增加三类稳健性检查。
13.1 成本情景
至少比较:
text
0 bps
2 bps / 单边换手
5 bps / 单边换手
10 bps / 单边换手
如果在线模型只在零成本下胜出,结论应写成“产生了更多毛信号”,而不是“更适应市场”。
13.2 相邻参数
不要做大规模寻优,只检查结论是否对相邻参数稳定,例如:
text
在线学习率:0.01 / 0.02 / 0.04
重训间隔:16 / 32 / 64
滚动窗口:120 / 240 / 480
门控损失倍数:1.05 / 1.10 / 1.20
如果优势只出现在一个非常窄的参数点,更可能是样本适配。
13.3 分阶段观察
把结果按波动率、趋势强度或自然时间区间拆分。
总样本收益可能掩盖两种完全不同的行为:模型在切换期恢复更快,但在稳定期因频繁更新持续漏损。只有分阶段观察,才能看见适应收益和噪声成本分别发生在哪里。
14. 这套实验仍然有哪些限制
第一,虚拟成交不是交易所撮合。下一根开盘附近入场、固定基点成本和无盘口冲击,都是简化假设。
第二,模型只预测下一根 K 线的开盘到收盘方向。这个目标方便做时间对齐,但不代表它是最有经济意义的预测目标。
第三,固定标准化器保证了模型比较的一致性,却没有解决特征分布漂移。在线标准化可以作为另一组实验,但不能只加入逐根模型。
第四,误差门控只是一条启发式规则,不具有漂移检测算法的统计保证。若要研究正式的漂移识别,应单独实现 ADWIN、DDM 等方法,并控制误报和检测延迟。
第五,单一品种、单一周期或单一回测区间无法回答“在线学习是否更好”。这类问题只能通过跨市场、跨阶段和相邻参数的稳定性来回答。
第六,逻辑回归的结论不应直接外推到深度模型。复杂模型有更强的表达能力,也有更大的更新自由度;在线更新的噪声风险通常不会因此自动消失。
第七,当前虚拟净值在较高换手成本下快速接近零,导致净收益和最大回撤指标出现饱和。后续研究应同时输出毛收益、累计成本、净对数收益以及每千根 K 线换手,避免不同模型都显示为接近 -100% 后无法继续比较。
结论
市场会变,不等于模型应该在每根 K 线后立即改变。
固定模型承担的是“停留在旧关系里”的风险;逐根在线模型承担的是“过度相信最近样本”的风险。两者之间不存在一个脱离数据、成本和预测目标的普遍答案。
在线更新是否有价值,至少要同时满足三点:
- 新环境出现后,预测损失能够更快恢复;
- 参数变化没有长期演化为更高的信号翻转和换手;
- 改善在成本后仍然存在,并能跨品种、周期和相邻参数重复。
本次默认参数回测中,逐根在线和滚动重训都发生了明显参数移动,却没有降低样本外 Log Loss;误差门控减少了换手和多空翻转,但仍未形成可交易优势。
如果模型只是更新得更勤、参数走得更远、交易做得更多,却没有持续降低样本外损失,那么它并没有更适应市场。
它只是更快地学会了最近发生的事情。
风险提示:本文讨论的是在线学习机制和回测方法,不构成投资建议。代码中的收益、成本和仓位均为研究用简化模型,不能替代真实成交、风控和交易所规则。
- 1

