深度神经网络 (第 I 部)。准备数据·进阶篇
🧹

深度神经网络 (第 I 部)。准备数据·进阶篇

(2/3)· 七成建模时间耗在喂数据,多数 DNN 失效源于第一阶段清洗与变换的草率

偏理论 第 2/3 篇
直接把终端导出的原始报价丢进神经网络,是多数 DNN 策略 silently 崩掉的根因。偏态分布和异常值不处理,模型学到的只是噪声的回音。外汇贵金属杠杆高,数据失真会被放大成实盘里的真实亏损。

「数字滤波权重与四类趋势线计算」

这一段给出的是数字滤波器(DigFiltr)所用的卷积权重序列,共 63 个数值,从 +0.0446941124 平滑递减到 -0.0107450280 附近,再回升至 +0.0073925495。权重关于中心不完全对称,说明该滤波器对近期与远端价格赋予非均匀衰减,倾向压制高频噪声而非简单移动平均。 代码中 type 参数决定调用哪条基线:1=fatl、2=rftl、3=satl、4=rstl,均为基于 Close 序列经同一套权重滚动卷积得到。n 为权重长度,m 为原始数据长度,k 经 rev(k) 反转后配合 rollApply 的右对齐窗口做 sum(x*k),输出前用 NA 左补至原长。 在 MT5 里可把这套权重直接塞进自定义指标 iCustom 的缓冲区,或抄进 EA 的数组做 OnCalculate 里的逐根卷积;外汇与贵金属杠杆高,滤波只是降噪,信号延后可能导致滑点扩大,实际触发概率需自行回测。 下方逐行拆解保留原逻辑: if (type == 1) {k = fatl} // 类型1取快速趋势线权重 if (type == 2) {k = rftl} // 类型2取慢速趋势线权重 if (type == 3) {k = satl} // 类型3取平滑趋势线权重 if (type == 4) {k = rstl} // 类型4取反转趋势线权重 n <- length(k) // 权重个数=63 m <- length(X) // 价格序列长度 k <- rev(k) // 反转权重以匹配时间顺序 f <- rowr::rollApply(data = X, fun = function(x) {sum(x * k)}, window = n, minimum = n, align = "right") // 右对齐滚动加权和 while (length(f) < m) { f <- c(NA,f)} // 前段补NA对齐 return(f) // 返回滤波结果 evalq(pr %<>% mutate(., fatl = DigFiltr(Close, 1), rftl = DigFiltr(Close, 2), satl = DigFiltr(Close, 3), rstl = DigFiltr(Close, 4)), env) // 给数据框算四条线 evalq(pr %<>% mutate(., ftlm = fatl - rftl, rbci = fatl - satl // 生成动量差与通道宽指标

MQL5 / C++
if (type == class="num">1) {k = fatl}
if (type == class="num">2) {k = rftl}
if (type == class="num">3) {k = satl}
if (type == class="num">4) {k = rstl}
n <- length(k)
m <- length(X)
k <- rev(k)
f <- rowr::rollApply(data = X,
                     fun = function(x) {sum(x * k)},
                     window = n, minimum = n, align = "right")
class="kw">while (length(f) < m) { f <- c(NA,f)}
class="kw">return(f)
}
evalq(pr %<>% mutate(.,
                     fatl = DigFiltr(Close, class="num">1),
                     rftl = DigFiltr(Close, class="num">2),
                     satl = DigFiltr(Close, class="num">3),
                     rstl = DigFiltr(Close, class="num">4)
                     ),
      env)
evalq(pr %<>% mutate(.,
                     ftlm = fatl - rftl,
                     rbci = fatl - satl,

差分序列与之字转向的拼接

在算出各平滑线的差值后,用 evalq 把一阶差分直接挂到 pr 数据框上:fatl、rftl、satl 的差分原样保留,而 rstl 的差分乘了 10 倍,目的是把慢速线的微小变动放大到与主序列同量级,方便后续同屏比对。 stlm 由 satl 减 rstl 得到,pcci 用收盘价减 fatl,这两个量分别刻画中速与慢速线的偏离和价格对快速线的穿透。再补一轮差分:ftlm、stlm、rbci、pcci 各自取一阶差,等于把「速度」再变成「加速度」,用于识别拐点临界。 之字转向函数 ZZ 接收 par=c(25,5),第一参数 25 若大于 1 会按 Dig 位数缩放为点数阈值,第二参数 5 代表取 High/Low 双列作转向依据。ZigZag 设 retrace=F、lastExtreme=T,回测样本 8000 根 K 线、30 个变量,dz 为 zigz 差分、sig 为其符号,可直接当趋势方向标签用。 外汇与贵金属波动剧烈,这类差分放大和之字阈值对滑点敏感,实盘前务必在 MT5 导出同源数据复算一遍 8000 根样本的结构是否一致。

MQL5 / C++
stlm = satl - rstl,
 pcci = Close - fatl,
 v.fatl = c(NA, diff(fatl)),
 v.rftl = c(NA, diff(rftl)),
 v.satl = c(NA, diff(satl)),
 v.rstl = c(NA, diff(rstl)*class="num">10)
 ),
 env)
evalq(pr %<>% mutate(.,
 v.ftlm = c(NA, diff(ftlm)),
 v.stlm = c(NA, diff(stlm)),
 v.rbci = c(NA, diff(rbci)),
 v.pcci = c(NA, diff(pcci))
 ),
 env)
#------ZZ-----------------------------------
par <- c(class="num">25, class="num">5)
ZZ <- function(x, par) {
# x - vector
 require(TTR)
 require(magrittr)
 ch = par[class="num">1]
 mode = par[class="num">2]
 if (ch > class="num">1) ch <- ch/(class="num">10 ^ (Dig - class="num">1))
 class="kw">switch(mode, xx <- x$Close,
 xx <- x$Med, xx <- x$Typ,
 xx <- x$Wd, xx <- x %>% select(High,Low))
 zz <- ZigZag(xx, change = ch, percent = F,
 retrace = F, lastExtreme = T)
 n <- class="num">1:length(zz)
 for (i in n) { if (is.na(zz[i])) zz[i] = zz[i - class="num">1]}
 class="kw">return(zz)
}
evalq(pr %<>% cbind(., zigz = ZZ(., par = par)), env)
evalq(pr %<>% cbind(., dz = diff(pr$zigz) %>% c(NA, .)), env)
evalq(pr %<>% cbind(., sig = sign(pr$dz)), env)

◍ R语言里把指标信号筛出来画图

上面这段输出是某次回测后导出的变量快照:fatl、rftl、satl、rstl 等 16 个序列里,前 15 个在首行全是 NA,只有 zigz 给了 123 的占位值,dz 从第二行起是 -0.0162,sig 连续 10 行都是 -1。这说明信号列在样本头部尚未激活,实际过滤要从 sig != 0 的位置开始。 用 dplyr 把 pr 转成 tbl 后,只挑 ftlm、stlm、rbci、pcci 及对应的 v.* 版本和 sig,再 filter(sig != 0),随后用 lead() 把 sig 错位成 Class 标签。这一步相当于把「当下信号」和「下一根 K 线归类」配对,方便后面做命中率统计。 画图部分直接 tail(200) 取最近 200 根:第一张纯 K 线标的是 EURJPY,第二张叠了 fatl(钢蓝)、rftl(红)、satl(金)、rstl(绿)、zigz(黑) 五条线。你在 MT5 里若用同名指标,可对照这条配色看均线带和之字转向是否同步。 oscillator 那张 dygraph 只取 ftlm/stlm/rbci/pcci 四个振荡器,fillAlpha=0.2 做半透明填充。外汇与贵金属波动大、杠杆高,这类图形仅用于辨识状态,实盘仍可能因滑点使信号失效。

MQL5 / C++
evalq(dataSet <- pr %>% tbl_df() %>%
       dplyr::select(Data, ftlm, stlm, rbci, pcci,
              v.fatl, v.satl, v.rftl, v.rstl,
              v.ftlm, v.stlm, v.rbci, v.pcci, sig) %>%
       dplyr::filter(., sig != class="num">0) %>%
       mutate(., Class = factor(sig, ordered = F) %>%
         dplyr::lead()) %>%
       dplyr::select(-sig),
       env)
evalq(pr %>% tail(., class="num">200) %>%
       ggplot(aes(x = Data, y = Close)) +
       geom_candlestick(aes(open = Open, high = High, low = Low, close = Close)) +
       labs(title = "EURJPY Candlestick Chart", y = "Close Price", x = "") +
       theme_tq(), env)
evalq(pr %>% tail(., class="num">200) %>%
       ggplot(aes(x = Data, y = Close)) +
       geom_candlestick(aes(open = Open, high = High, low = Low, close = Close)) +
       geom_line(aes(Data, fatl), class="type">class="kw">color = "steelblue", size = class="num">1) +
       geom_line(aes(Data, rftl), class="type">class="kw">color = "red", size = class="num">1) +
       geom_line(aes(Data, satl), class="type">class="kw">color = "gold", size = class="num">1) +
       geom_line(aes(Data, rstl), class="type">class="kw">color = "green", size = class="num">1) +
       geom_line(aes(Data, zigz), class="type">class="kw">color = "black", size = class="num">1) +
       labs(title = "EURJPY Candlestick Chart",
            subtitle = "Combining Chart Geoms",
            y = "Close Price", x = "") +
       theme_tq(), env)
require(dygraphs)
evalq(dataSet %>% tail(., class="num">200) %>% tk_tbl %>%
       select(Data, ftlm, stlm, rbci, pcci) %>%
       tk_xts() %>%
       dygraph(., main = "Oscilator base") %>%
       dyOptions(.,
         fillGraph = TRUE,
         fillAlpha = class="num">0.2,
         drawGapEdgePoints = TRUE,
         colors = c("green", "violet", "red", "blue"),

「用 dygraph 把多组振荡器叠成可交互图」

这段 R 代码把前面算好的指标集按时间窗口拆成三张动态图,方便肉眼比对快慢线和中轴带的背离。 evalq 里第一张图取全量 dataSet 的尾 200 行,挑出 v.fatl、v.satl、v.rftl、v.rstl 四列,转成 xts 后交给 dygraph,主标题写 Oscilator 2。 第二张图只取尾 100 行,列换成 v.ftlm、v.stlm、v.rbci、v.pcci,标题 Oscilator 3;两张都设 fillGraph=TRUE、fillAlpha=0.2,即在曲线下方填 20% 透明色块。 颜色固定为绿、紫、红、深蓝四色,digitsAfterDecimal 由变量 Dig 控制小数位,dyLegend 设 show="always" 且鼠标移出仍保留图例。在 MT5 外接 R 环境里跑这套,能把外汇或贵金属的高风险品种振荡结构拉出来肉眼验背离。

MQL5 / C++
evalq(dataSet %>% tail(., class="num">200) %>% tk_tbl %>%%
  select(Data, v.fatl, v.satl, v.rftl, v.rstl) %>%%
  tk_xts() %>%%
  dygraph(., main = "Oscilator class="num">2") %>%%
  dyOptions(.,%
             fillGraph = TRUE,%
             fillAlpha = class="num">0.2,%
             drawGapEdgePoints = TRUE,%
             colors = c("green", "violet", "red", "darkblue"),%
             digitsAfterDecimal = Dig) %>%%
  dyLegend(show = "always",%
           hideOnMouseOut = TRUE),%
  env)
evalq(dataSet %>% tail(., class="num">200) %>% tk_tbl %>%%
  select(Data, v.fatl, v.satl, v.rftl, v.rstl) %>%%
  tk_xts() %>%%
  dygraph(., main = "Oscilator class="num">2") %>%%
  dyOptions(.,%
             fillGraph = TRUE,%
             fillAlpha = class="num">0.2,%
             drawGapEdgePoints = TRUE,%
             colors = c("green", "violet", "red", "darkblue"),%
             digitsAfterDecimal = Dig) %>%%
  dyLegend(show = "always",%
           hideOnMouseOut = TRUE),%
  env)
evalq(dataSet %>% tail(., class="num">100) %>% tk_tbl %>%%
  select(Data, v.ftlm, v.stlm, v.rbci, v.pcci) %>%%
  tk_xts() %>%%
  dygraph(., main = "Oscilator class="num">3") %>%%
  dyOptions(.,%
             fillGraph = TRUE,%
             fillAlpha = class="num">0.2,%
             drawGapEdgePoints = TRUE,%
             colors = c("green", "violet", "red", "darkblue"),%
             digitsAfterDecimal = Dig) %>%%
  dyLegend(show = "always",%
           hideOnMouseOut = TRUE),%
  env)

用问题驱动预测因子的分布排查

做外汇或贵金属模型前,先别急着跑回测。把数据理解这件事交给探索性分析(EDA):用提问当探针,每抛出一个问题就逼自己只看数据的某一截面,图表和变换手段自然跟着问题走。 创意过程里,好问题都是堆出来的。一开始你根本不知道样本里藏了什么结论,但每多问一句“这个变量怎么变”“它和别的变量怎么协变”,就多撕开一层视角。EURUSD 的 14 个高频预测因子,靠连续追问能很快滑到最反常的那几列。 变化指同变量在不同测量下的偏移趋势;协变指变量间的联动。哪怕测光速七次也会拿到七个带微误的值,所以看分布比盯单点重要。下面这段 R/PerformanceAnalytics 风格的总体统计,是顺手用 table.Stats 拉出的四个预测器快照。 表格里几个硬数值得记:ftlm 观测 7955、NA 42,偏度 4.21、峰度 84.61;pcci 观测 7960、NA 37,偏度 6.47、峰度 247.42。所有预测器都带明显正态偏离和高峰度,IQR 很窄却挂着显眼异常值——这种形状在 MT5 导出的 tick 特征里很常见,手动目检前先信这几个数。 别把正态当圣经 峰度破百说明尾部厚得离谱,直接套正态假设会低估贵金属跳空风险。先把异常值单独标出来,再决定丢还是留。

MQL5 / C++
> table.Stats(env$dataSet %>% tk_xts())
Using column `Data` for date_var.
                     ftlm       stlm       rbci       pcci
Observations     class="num">7955.0000 class="num">7908.0000 class="num">7934.0000 class="num">7960.0000
NAs                 class="num">42.0000   class="num">89.0000   class="num">63.0000   class="num">37.0000
Minimum            -class="num">0.7597   -class="num">1.0213   -class="num">0.9523   -class="num">0.5517
Quartile class="num">1         -class="num">0.0556   -class="num">0.1602   -class="num">0.0636   -class="num">0.0245
Median             -class="num">0.0001    class="num">0.0062   -class="num">0.0016   -class="num">0.0001
Arithmetic Mean     class="num">0.0007    class="num">0.0025    class="num">0.0007    class="num">0.0001
Geometric Mean    -class="num">0.0062       NaN   -class="num">0.0084   -class="num">0.0011
Quartile class="num">3          class="num">0.0562    class="num">0.1539    class="num">0.0675    class="num">0.0241
Maximum             class="num">2.7505    class="num">3.0407    class="num">2.3872    class="num">1.8859
SE Mean             class="num">0.0014    class="num">0.0033    class="num">0.0015    class="num">0.0006
LCL Mean(class="num">0.95)   -class="num">0.0020   -class="num">0.0040   -class="num">0.0022   -class="num">0.0010
UCL Mean(class="num">0.95)    class="num">0.0034    class="num">0.0090    class="num">0.0035    class="num">0.0012
Variance           class="num">0.0152    class="num">0.0858    class="num">0.0172    class="num">0.0026
Stdev             class="num">0.1231    class="num">0.2929    class="num">0.1311    class="num">0.0506
Skewness          class="num">4.2129    class="num">1.7842    class="num">2.3037    class="num">6.4718
Kurtosis         class="num">84.6116   class="num">16.7471   class="num">45.0133  class="num">247.4208
                    v.fatl     v.satl     v.rftl     v.rstl
Observations     class="num">7959.0000 class="num">7933.0000 class="num">7954.0000 class="num">7907.0000

◍ 四个指标的统计分布长什么样

把四组指标(v.ftlm、v.stlm、v.rbci、v.pcci)拉进描述统计,样本量都在 7900 笔以上,缺失值最多 90 笔。算术均值全部贴着 0(0.0002~0.0011),但几何均值有三项为负,说明序列里负向拖尾比正向更密,复利视角下偏向亏损。 偏度全正且极高:v.ftlm 达 5.26,v.rbci 也有 3.95,峰度更是夸张,v.ftlm 的峰度 145.84、v.stlm 36.94。这直接戳破「残差近似正态」的假设——极端跳动的概率远高于高斯分布预期,外汇与贵金属这种品种的高风险正藏在这里。 标准差看,v.pcci 的 Stdev 0.1372 是四者最大,最小的是 v.stlm 的 0.0219,差了 6 倍多。若你拿这些序列做阈值过滤,v.stlm 的噪声带要收得比 v.pcci 窄得多才不容易误触发。 第二组统计里 v.pcci 的几何均值算出 NaN,原因是该列含非正数值导致对数变换失效。碰到这种情况别硬算几何均值,直接切回算术均值或做符号分离处理更稳妥。

把重复劳动交给小布
这些诊断与清洗流程小布盯盘的 AIGC 已内置,打开对应品种页即可看到分布偏度和异常点标记,你只需决定保留还是剔除。

常见问题

因为模型性能上限由输入质量决定,报价缺失、异常值和偏态若不处理,后续训练只是在拟合噪声,实盘概率倾向失效。
小布可自动拉取终端数据、计算总体统计并标出异常值,但偏态消除和目标变量定义仍需交易者按品种特性设定。
darch 包(v0.12)提供灵活的多层全连接网络构建与预训练接口,适合本文所述的严格分阶段实验流程。
倾向要做,单纯看总体统计容易漏掉局部聚集异常,图形能暴露分布扭曲和离群结构,降低后续清理盲区。