深度神经网络 (第 I 部)。准备数据·进阶篇
(2/3)· 七成建模时间耗在喂数据,多数 DNN 失效源于第一阶段清洗与变换的草率
「数字滤波权重与四类趋势线计算」
这一段给出的是数字滤波器(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 // 生成动量差与通道宽指标
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 根样本的结构是否一致。
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 做半透明填充。外汇与贵金属波动大、杠杆高,这类图形仅用于辨识状态,实盘仍可能因滑点使信号失效。
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 环境里跑这套,能把外汇或贵金属的高风险品种振荡结构拉出来肉眼验背离。
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 特征里很常见,手动目检前先信这几个数。 别把正态当圣经 峰度破百说明尾部厚得离谱,直接套正态假设会低估贵金属跳空风险。先把异常值单独标出来,再决定丢还是留。
> 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,原因是该列含非正数值导致对数变换失效。碰到这种情况别硬算几何均值,直接切回算术均值或做符号分离处理更稳妥。