数据科学和机器学习(第 37 部分):利用烛条形态和人工智能战胜市场·综合运用
(3/3)·从单根柱线形态编码到机器学习预测,再到交易机器人终结,这一步走完才谈得上闭环
「用 K 线形态向量喂给 CatBoost 下单」
这段逻辑跑在 OnTick 里,先把上一根已完成 K 线(索引 1)的 OHLC 抓出来,作为形态识别的输入源。 具体用了 CTALib 里的 10 个蜡烛函数:白线、黑线、十字星、蜻蜓十字、墓碑十字、锤子、倒锤、纺锤、看涨光头光脚、看跌光头光脚,拼成一个 vector x。训练侧对应的列名就是 White Candle 到 BullishMarubozu 这 10 维特征,维度必须对齐否则预测会错位。 x 丢进 catboost.predict(x).cls 拿到信号类:1 倾向做多、0 倾向做空。开仓前用 PosExists 查当前有无同反向持仓,无仓才用 m_trade.Buy/Sell 以最小手数 volume_ 进场,止损止盈先传 0 留给后续管理。 平仓靠 CloseTradeAfterTime,参数是「周期分钟数 × lookahead × 60」秒,也就是按训练时用的 timeframe 和前瞻窗口强制收单。外汇与贵金属杠杆高,信号只是概率倾向,实盘前务必在 MT5 策略测试器用历史数据验证信号分布。
class="type">void OnTick() { class=class="str">"cmt">//--- class="type">class="kw">double open = iOpen(Symbol(), Period(), class="num">1), high = iHigh(Symbol(), Period(), class="num">1), low = iLow(Symbol(), Period(), class="num">1), close = iClose(Symbol(), Period(), class="num">1); vector x = { CTALib::CDLWHITECANDLE(open, close), CTALib::CDLBLACKCANDLE(open, close), CTALib::CDLDOJI(open, close), CTALib::CDLDRAGONFLYDOJI(open, high, low, close), CTALib::CDLGRAVESTONEDOJI(open, high, low, close), CTALib::CDLHAMMER(open, high, low, close), CTALib::CDLINVERTEDHAMMER(open, high, low, close), CTALib::CDLSPINNINGTOP(open, high, low, close), CTALib::CDLBULLISHMARUBOZU(open, high, low, close), CTALib::CDLBEARISHMARUBOZU(open, high, low, close) }; class="type">long signal = catboost.predict(x).cls; class=class="str">"cmt">//Predicted class class="type">MqlTick ticks; if (!SymbolInfoTick(Symbol(), ticks)) { printf("Failed to obtain ticks information, Error = %d",GetLastError()); class="kw">return; } class="type">class="kw">double volume_ = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); if (signal == class="num">1) { if (!PosExists(POSITION_TYPE_BUY) && !PosExists(POSITION_TYPE_SELL)) m_trade.Buy(volume_, Symbol(), ticks.ask,class="num">0,class="num">0); } if (signal == class="num">0) { if (!PosExists(POSITION_TYPE_SELL) && !PosExists(POSITION_TYPE_BUY)) m_trade.Sell(volume_, Symbol(), ticks.bid,class="num">0,class="num">0); } CloseTradeAfterTime((Timeframe2Minutes(Period())*lookahead)*class="num">60); class=class="str">"cmt">//Close the trade after a certain lookahead and according the the trained timeframe }
◍ 把K线形态喂给模型前先过一道滤网
这段逻辑在 OnTick 里先取了前一根已收盘K线(索引1)的 open/high/low/close,用 CTALib 里的一堆形态函数把单根K线转成特征向量 x,再丢给 catboost 做分类预测,返回 signal 作为大方向。 但注意被高亮的那段:真正下单前还有第二道闸门。代码里另外建了 patterns 向量,只装了十字、蜻蜓十字、墓碑十字、锤子、倒锤、纺锤、看涨/看跌marubozu这8种特殊形态,并在开仓条件里要求 patterns.Sum() > 0。也就是说,模型喊多(signal==1)还不够,当前K线必须至少命中一种特殊形态才放单。 外汇和贵金属波动受消息面干扰大,这种双确认能砍掉一部分模型在平淡K线上的假信号,但特殊形态本身在欧盘流动性差时也常失效,实际触发频率可能偏低,建议先在 MT5 策略测试器里跑一周 tick 数据看 patterns.Sum()>0 的命中率再决定阈值。
class="type">void OnTick() { class=class="str">"cmt">//--- class="type">class="kw">double open = iOpen(Symbol(), Period(), class="num">1), high = iHigh(Symbol(), Period(), class="num">1), low = iLow(Symbol(), Period(), class="num">1), close = iClose(Symbol(), Period(), class="num">1); vector x = { CTALib::CDLWHITECANDLE(open, close), CTALib::CDLBLACKCANDLE(open, close), CTALib::CDLDOJI(open, close), CTALib::CDLDRAGONFLYDOJI(open, high, low, close), CTALib::CDLGRAVESTONEDOJI(open, high, low, close), CTALib::CDLHAMMER(open, high, low, close), CTALib::CDLINVERTEDHAMMER(open, high, low, close), CTALib::CDLSPINNINGTOP(open, high, low, close), CTALib::CDLBULLISHMARUBOZU(open, high, low, close), CTALib::CDLBEARISHMARUBOZU(open, high, low, close) }; vector patterns = { CTALib::CDLDOJI(open, close), CTALib::CDLDRAGONFLYDOJI(open, high, low, close), CTALib::CDLGRAVESTONEDOJI(open, high, low, close), CTALib::CDLHAMMER(open, high, low, close), CTALib::CDLINVERTEDHAMMER(open, high, low, close), CTALib::CDLSPINNINGTOP(open, high, low, close), CTALib::CDLBULLISHMARUBOZU(open, high, low, close), CTALib::CDLBEARISHMARUBOZU(open, high, low, close) }; class=class="str">"cmt">//Store all the special patterns class="type">long signal = catboost.predict(x).cls; class=class="str">"cmt">//Predicted class class="type">MqlTick ticks; if (!SymbolInfoTick(Symbol(), ticks)) { printf("Failed to obtain ticks information, Error = %d",GetLastError()); class="kw">return; } class="type">class="kw">double volume_ = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); if (signal == class="num">1 && patterns.Sum()>class="num">0) class=class="str">"cmt">//Check if there are is atleast a special pattern before opening a trade {
空仓时才按信号开单
这段逻辑卡的是「账户里没单才动手」的边界。signal 为 1 且 patterns.Sum() 大于 0 时,先确认当前既没有多单也没有空单,才用 m_trade.Buy 以 ticks.ask 市价买入;signal 为 0 且形态累加大于 0 时,对称地检查无空无多后才 m_trade.Sell 以 ticks.bid 市价卖出。 两个分支都调 PosExists 做仓位闸门,避免在同一根 K 线里重复追单把仓位叠厚。CloseTradeAfterTime 那行按训练时框定的周期换算秒数强平,例如 Period() 返回 5 分钟、lookahead 设为 3,则 (5*3)*60 = 900 秒后离场。 外汇与贵金属杠杆高,信号为 0 或 1 只代表模型倾向,实盘前请在 MT5 策略测试器用历史 tick 跑一遍这套开仓条件,确认 PosExists 的返回逻辑和你账户实际持仓枚举一致。
if (!PosExists(POSITION_TYPE_BUY) && !PosExists(POSITION_TYPE_SELL)) m_trade.Buy(volume_, Symbol(), ticks.ask,class="num">0,class="num">0); } if (signal == class="num">0 && patterns.Sum()>class="num">0) class=class="str">"cmt">//Check if there are is atleast a special pattern before opening a trade { if (!PosExists(POSITION_TYPE_SELL) && !PosExists(POSITION_TYPE_BUY)) m_trade.Sell(volume_, Symbol(), ticks.bid,class="num">0,class="num">0); } CloseTradeAfterTime((Timeframe2Minutes(Period())*lookahead)*class="num">60); class=class="str">"cmt">//Close the trade after a certain lookahead and according the the trained timeframe }
「把烛形喂给模型前先想清楚的事」
用 AI 做烛条形态预测确实跑得通,但和均线、MACD 这类数学指标不同,烛形靠的是“长得像不像”,一点视觉误读就可能让标签完全反转。评论区有人拿 D1 和 H4 举过例子:同一段报价历史,仅把小时内的分钟人为平移,形态就能从锤子变成蜻蜓十字星,经纪商时区偏移也会干同样的事。 作者自己在讨论里补了一刀:纯按外观打标签不够,锤子要当看涨信号至少得处在下降趋势里,趋势检测才是等式里的关键项。如果你打算用附件里的 Candlestick Patterns Collect.mq5 去导 CSV 训 Catboost,先把这段逻辑写进标注规则,不然模型学到的只是形状偏见。 附件里 Experts\CandlestickPatterns AI-EA.mq5 已经能加载 .onnx 跑预测,Indicators\Candlestick Identifier.mq5 可在图上肉眼核对形态。外汇和贵金属杠杆高、滑点狠,这类模型输出只代表概率倾向,开 MT5 接上脚本跑一轮自有数据再下结论。