数据科学和机器学习(第 37 部分):利用烛条形态和人工智能战胜市场·进阶篇
🕯️

数据科学和机器学习(第 37 部分):利用烛条形态和人工智能战胜市场·进阶篇

(2/3)·单根烛条 10 种形态如何编码、收集并喂给模型,多数教程卡在检测这一步

偏理论进阶 第 2/3 篇

不少交易者把阳线阴线当预测信号,其实单根烛条本身不指向未来。本篇接上篇概念,继续把烛条形态转成可被机器学习消费的工程数据,并推进到交易机器人。

◍ 把烛条形态画到主图上才算真懂

做价格行为分析时,烛台形态常被讲得玄乎,但真要拿去喂机器学习模型,数据品质比肉眼感觉重要得多。一个直接的办法是用代码把形态在主图可视化,让人和机器对得上账。 这个指标挂在主图窗口,开了 5 个缓冲区和 1 个绘图序列:阳线默认橙色、阴线蓝色,一旦识别出形态就把该 K 线标红,并打一个 90 度旋转的文字标签写明形态名。 ta-lib.mqh 是静态类,不用初始化,直接在 OnCalculate 里调 CDL 开头的形态函数就行。下面这段节选能看出来,它先按收盘大于开盘填颜色缓冲,再用 20% 波幅做 padding 把 Doji 标签放到高点上方。 肉眼回看图表,红色 K 线基本就是人能在盘面上圈出来的那些形态,说明逻辑没跑偏。下一步是把这些识别结果用脚本落盘成 CSV,再拿去训模型——外汇和贵金属波动大、杠杆高,形态信号只是概率倾向,实盘前务必在 MT5 历史数据里自己跑一遍验证。

MQL5 / C++
class="macro">#class="kw">property indicator_chart_window
class="macro">#class="kw">property indicator_buffers class="num">5
class="macro">#class="kw">property indicator_plots class="num">1
class="macro">#class="kw">property indicator_type1 DRAW_COLOR_CANDLES
class="macro">#class="kw">property indicator_color1 clrDodgerBlue, clrOrange, clrRed
class="macro">#class="kw">property indicator_style1  STYLE_SOLID
class="macro">#class="kw">property indicator_width1  class="num">1
class="type">class="kw">double OpenBuff[];
class="type">class="kw">double HighBuff[];
class="type">class="kw">double LowBuff[];
class="type">class="kw">double CloseBuff[];
class="type">class="kw">double ColorBuff[];
class="macro">#include <ta-lib.mqh> class=class="str">"cmt">//!important for candlestick patterns
class="type">int OnCalculate(const class="type">int rates_total,
                const class="type">int prev_calculated,
                const class="type">class="kw">datetime &time[],
                const class="type">class="kw">double &open[],
                const class="type">class="kw">double &high[],
                const class="type">class="kw">double &low[],
                const class="type">class="kw">double &close[],
                const class="type">long &tick_volume[],
                const class="type">long &volume[],
                const class="type">int &spread[])
  {
class=class="str">"cmt">//---
  if (rates_total<class="num">1)
    class="kw">return rates_total;

   for(class="type">int i = prev_calculated; i < rates_total; i++)
   {
    OpenBuff[i]  = open[i];
    HighBuff[i]  = high[i];
    LowBuff[i]   = low[i];
    CloseBuff[i] = close[i];

    class=class="str">"cmt">//---

    if (close[i]>open[i])
       ColorBuff[i] = class="num">1.0;
    else
       ColorBuff[i] = class="num">0.0;

    class=class="str">"cmt">//---

    class="type">class="kw">double padding = MathAbs(high[i] - low[i]) * class="num">0.2; class=class="str">"cmt">// class="num">20% padding

    if (CTALib::CDLDOJI(open[i], close[i]))
      {
       TextCreate(class="type">class="kw">string(i)+(class="type">class="kw">string)time[i], time[i]-PeriodSeconds(), high[i]+padding, "Doji", clrBlack, class="num">90.0);
       ColorBuff[i] = class="num">2.0;
      }

    if (CTALib::CDLDRAGONFLYDOJI(open[i], high[i], low[i], close[i]))
      {

「把反转形态标记到K线上的写法」

这段逻辑跑在指标的主循环里,对每根柱线 i 依次调用 CTALib 里的形态识别函数,命中就在 high[i]+padding 的位置用 TextCreate 写形态名,并把 ColorBuff[i] 置为 2.0 做统一着色。 具体覆盖了蜻蜓十字、墓碑十字、锤子线、倒锤子、纺锤线,以及多空Marubozu。其中纺锤线传了 0.3 和 2.0 两个阈值,Marubozu 传了整数 2 作为实体占比容差,这些参数直接决定形态判定的松紧。 所有标注都用 clrBlack 黑色、90.0 角度绘制,name 用序号拼 time[i] 保证唯一不重叠。外汇和贵金属波动大,这类标注只是形态提示,后续反转概率仍需结合上下文验证,开 MT5 把这段代码贴进自定义指标即可看到实时标记。

MQL5 / C++
TextCreate(class="type">class="kw">string(i)+(class="type">class="kw">string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"DragonFly Doji", clrBlack, class="num">90.0);
ColorBuff[i] = class="num">2.0;
}

if (CTALib::CDLGRAVESTONEDOJI(open[i], high[i], low[i], close[i]))
{
TextCreate(class="type">class="kw">string(i)+(class="type">class="kw">string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"GraveStone Doji", clrBlack, class="num">90.0);
ColorBuff[i] = class="num">2.0;
}

if (CTALib::CDLHAMMER(open[i], high[i], low[i], close[i]))
{
TextCreate(class="type">class="kw">string(i)+(class="type">class="kw">string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Hammer", clrBlack, class="num">90.0);
ColorBuff[i] = class="num">2.0;
}

if (CTALib::CDLINVERTEDHAMMER(open[i], high[i], low[i], close[i]))
{
TextCreate(class="type">class="kw">string(i)+(class="type">class="kw">string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Inverted Hammer", clrBlack, class="num">90.0);
ColorBuff[i] = class="num">2.0;
}

if (CTALib::CDLSPINNINGTOP(open[i], high[i], low[i], close[i], class="num">0.3, class="num">2.0))
{
TextCreate(class="type">class="kw">string(i)+(class="type">class="kw">string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Spinning Top", clrBlack, class="num">90.0);
ColorBuff[i] = class="num">2.0;
}

if (CTALib::CDLBULLISHMARUBOZU(open[i], high[i], low[i], close[i], class="num">2))
{
TextCreate(class="type">class="kw">string(i)+(class="type">class="kw">string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Bullish Marubozu", clrBlack, class="num">90.0);
ColorBuff[i] = class="num">2.0;
}

if (CTALib::CDLBEARISHMARUBOZU(open[i], high[i], low[i], close[i], class="num">2))
{
TextCreate(class="type">class="kw">string(i)+(class="type">class="kw">string)time[i], time[i]-PeriodSeconds(), high[i]+padding,"Bearish Marubozu", clrBlack, class="num">90.0);
ColorBuff[i] = class="num">2.0;
}
}

class=class="str">"cmt">//--- class="kw">return value of prev_calculated for next call
class="kw">return(rates_total);
}

用 18 年日线喂饱形态样本

较高时间帧的烛条形态本身低频,拿周线或日线硬凑几年数据,机器学习多半会饿死。这里直接拉了 2005.01.01 到 2023.01.01 的 XAUUSD 日线,18 年大约 4600+ 根柱线,对日线级形态识别算够厚的一层底料。 除形态标签外,OHLC 四价也一并落盘。不是每套模型都用得到,但后面若想做「形态出现后 N 根收益分布」或特征交叉,缺了原始价格就只能重跑,不如一次写全。 下面这段 MT5 脚本把 TA-Lib 的烛条识别封装和 Pandas 风格 DataFrame 都引了进来,输入段锁死品种、周期与起止日,OnStart 里先 CopyRates 拉四个价格向量,再逐个调 CTALib 的形态函数塞进 DataFrame,最后 to_csv 导出。 #include <ta-lib.mqh> // 引入 CTALib 类,封装了各类烛条形态识别函数 #include <MALE5\Pandas\pandas.mqh> // 引入 DataFrame 风格容器,便于按列组织形态与价格 input datetime start_date = D'2005.01.01'; // 回测样本起点:2005年1月1日 input datetime end_date = D'2023.01.01'; // 回测样本终点:2023年1月1日 input string symbol = "XAUUSD"; // 目标品种:黄金现货美元报价 input ENUM_TIMEFRAMES timeframe = PERIOD_D1; // 时间帧:日线 void OnStart() { //--- 脚本主入口 vector open, high, low, close; // 声明四个价格向量 open.CopyRates(symbol, timeframe, COPY_RATES_OPEN, start_date, end_date); // 拉取开盘价序列 high.CopyRates(symbol, timeframe, COPY_RATES_HIGH, start_date, end_date); // 拉取最高价序列 low.CopyRates(symbol, timeframe, COPY_RATES_LOW, start_date, end_date); // 拉取最低价序列 close.CopyRates(symbol, timeframe, COPY_RATES_CLOSE, start_date, end_date); // 拉取收盘价序列 CDataFrame df; // 建一个 DataFrame 装所有列 vector cdl_patterns = {}; // 临时向量存形态返回值 cdl_patterns = CTALib::CDLWHITECANDLE(open, close); // 白烛形态识别 df.insert("White Candle", cdl_patterns); // 写入列 cdl_patterns = CTALib::CDLBLACKCANDLE(open, close); // 黑烛形态识别 df.insert("Black Candle", cdl_patterns); cdl_patterns = CTALib::CDLDOJI(open, close); // 十字星识别 df.insert("Doji Candle", cdl_patterns); cdl_patterns = CTALib::CDLDRAGONFLYDOJI(open, high, low, close); // 蜻蜓十字识别 df.insert("Dragonflydoji Candle", cdl_patterns); cdl_patterns = CTALib::CDLGRAVESTONEDOJI(open, high, low, close); // 墓碑十字识别 df.insert("Gravestonedoji Candle", cdl_patterns); cdl_patterns = CTALib::CDLHAMMER(open, high, low, close); // 锤子线识别 df.insert("Hammer Candle", cdl_patterns); cdl_patterns = CTALib::CDLINVERTEDHAMMER(open, high, low, close); // 倒锤子识别 df.insert("Invertedhammer Candle", cdl_patterns); cdl_patterns = CTALib::CDLSPINNINGTOP(open, high, low, close); // 纺锤线识别 df.insert("Spinningtop Candle", cdl_patterns); cdl_patterns = CTALib::CDLBULLISHMARUBOZU(open, high, low, close); // 看涨光头光脚识别 df.insert("BullishMarubozu Candle", cdl_patterns); cdl_patterns = CTALib::CDLBEARISHMARUBOZU(open, high, low, close); // 看跌光头光脚识别 df.insert("BearishMarubozu Candle", cdl_patterns); df.insert("Open", open); // 存开盘价 df.insert("High", high); // 存最高价 df.insert("Low", low); // 存最低价 df.insert("Close", close); // 存收盘价 df.to_csv(StringFormat("CandlestickPatterns.%s.%s.csv",symbol,EnumToString(timeframe)), true); // 导出为 CSV,含表头 } 实盘前先把脚本丢进 MT5 的脚本目录编译,跑完会在终端目录生成 CandlestickPatterns.XAUUSD.PERIOD_D1.csv。外汇与贵金属杠杆高、跳空频繁,样本外的形态失效概率不低,这份 CSV 只解决「有没有数据」的问题,不暗示任何形态胜率。

MQL5 / C++
class="macro">#include <ta-lib.mqh> class=class="str">"cmt">//Contains CTALib class for candlestick patterns detection
class="macro">#include <MALE5\Pandas\pandas.mqh> class=class="str">"cmt">//[MQL5官方文档]
input class="type">class="kw">datetime start_date = D&class="macro">#x27;class="num">2005.01.class="num">01&class="macro">#x27;;
input class="type">class="kw">datetime end_date = D&class="macro">#x27;class="num">2023.01.class="num">01&class="macro">#x27;;
input class="type">class="kw">string symbol = "XAUUSD";
input ENUM_TIMEFRAMES timeframe = PERIOD_D1;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script program start function                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
class=class="str">"cmt">//---
   
   vector open, high, low, close;
   
   open.CopyRates(symbol, timeframe, COPY_RATES_OPEN, start_date, end_date);
   high.CopyRates(symbol, timeframe, COPY_RATES_HIGH, start_date, end_date);
   low.CopyRates(symbol, timeframe, COPY_RATES_LOW, start_date, end_date);
   close.CopyRates(symbol, timeframe, COPY_RATES_CLOSE, start_date, end_date);
   
   CDataFrame df;
   
   vector cdl_patterns = {};
   cdl_patterns = CTALib::CDLWHITECANDLE(open, close);
   df.insert("White Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLBLACKCANDLE(open, close);
   df.insert("Black Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLDOJI(open, close);
   df.insert("Doji Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLDRAGONFLYDOJI(open, high, low, close);
   df.insert("Dragonflydoji Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLGRAVESTONEDOJI(open, high, low, close);
   df.insert("Gravestonedoji Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLHAMMER(open, high, low, close);
   df.insert("Hammer Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLINVERTEDHAMMER(open, high, low, close);
   df.insert("Invertedhammer Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLSPINNINGTOP(open, high, low, close);
   df.insert("Spinningtop Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLBULLISHMARUBOZU(open, high, low, close);
   df.insert("BullishMarubozu Candle", cdl_patterns);
   
   cdl_patterns = CTALib::CDLBEARISHMARUBOZU(open, high, low, close);
   df.insert("BearishMarubozu Candle", cdl_patterns);
   
   df.insert("Open", open);
   df.insert("High", high);
   df.insert("Low", low);
   df.insert("Close", close);
   
   df.to_csv(StringFormat("CandlestickPatterns.%s.%s.csv",symbol,EnumToString(timeframe)), true);
  }

◍ 用 CatBoost 啃烛条形态的预测标签怎么造

拿到日线烛条数据集后,最棘手的不是跑模型,而是怎么定义「目标变量」。常见做法是引一个 lookahead 参数,指定往前看几根柱线:lookahead=1 时,只要下一根收盘价高于当前收盘价,就打标签 1(看涨),否则打 0。 原始数据里除了阳线、阴线,绝大多数行的特殊形态列全是 0,直接喂给模型会逼它过度依赖阴阳线。丢光全零行是一种解法;给「无形态」行加 -1 的保持类信号又会引爆类不平衡,两条路都不干净。 作者选了 CatBoost 分类器,理由是类别列多、契合度高。测试集上类 1 精度 0.58、类 0 仅 0.49,整体准确率 53%——比随机猜的 50% 略强,类 0 不如直接抛硬币。特征重要性里陀螺烛最冲、十字星次之、光头阴线最弱。 外汇与贵金属属高风险品种,53% 的准确率不代表可裸奔进场,必须配合止损或固定根数平仓。lookahead 试过大于 1 反而掉点,最终锁死在 1,用下一根柱线作预测窗口。 别把零值行当噪音直接扔 全零行里藏着「无特殊形态」这个真实市场状态,硬删会令模型在实盘遇到普通阳阴线时丧失上下文。更稳的是在特征工程阶段单独标记,而非塞进标签制造伪平衡。

MQL5 / C++
class="kw">import pandas as pd
symbol = "XAUUSD"
df = pd.read_csv(f"/kaggle/input/forex-candlestick-patterns/CandlestickPatterns.{symbol}.PERIOD_D1.csv")
df
lookahead = class="num">1
new_df = df.copy()
new_df["future_close"] = new_df["Close"].shift(-lookahead)
new_df.dropna(inplace=True)  # Drop NaNs caused by the shift operation
signal = []
for i in range(len(new_df)):  # Iterate over rows, not columns
    if new_df["future_close"].iloc[i] > new_df["Close"].iloc[i]:
        signal.append(class="num">1)
    else:
        signal.append(class="num">0)
new_df["Signal"] = signal
X = new_df.drop(columns=[
    "Signal",
    "Open",
    "High",
    "Low",
    "Close",
    "future_close"
])
y = new_df["Signal"]
# Split data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=class="num">0.2, random_state=class="num">42, shuffle=False)
from catboost class="kw">import CatBoostClassifier
from sklearn.utils.class_weight class="kw">import compute_class_weight
# Automatically calculate class weights
classes = np.unique(y)
weights = compute_class_weight(class_weight=&class="macro">#x27;balanced&class="macro">#x27;, classes=classes, y=y)
class_weights = dict(zip(classes, weights))
# Define the base model
model = CatBoostClassifier(
    iterations=class="num">1000,
    learning_rate=class="num">0.01,
    depth=class="num">5,
    loss_function=&class="macro">#x27;Logloss&class="macro">#x27;,
    class_weights=class_weights,
    verbose=class="num">100
)
model.fit(X_train, y_train) # Training the classifier
class="num">0:      learn: class="num">0.6930586      total: class="num">3.64ms   remaining: class="num">3.64s
class="num">100:    learn: class="num">0.6897625      total: 136ms    remaining: class="num">1.21s
class="num">200:    learn: class="num">0.6888030      total: 269ms    remaining: class="num">1.07s
class="num">300:    learn: class="num">0.6883559      total: 401ms    remaining: 931ms

「CatBoost 训练日志与特征重要性落地」

从迭代 400 到 999,learn 指标从 0.6881469 缓降到 0.6876900,总耗时由 532ms 爬到 1.3s,剩余时间同步收敛到 0。这种逐百步的微小下降说明模型在后期已接近收敛,继续加树大概率只是吃算力。 测试集分类报告里 accuracy 只有 0.53,类 0 的 precision 0.49、recall 0.55,类 1 的 precision 0.58、recall 0.52,support 合计 940(429+511)。在外汇与贵金属这种高噪声品种上,单模型五成出头的命中率属于常态,杠杆交易风险极高,别拿这个准确率当进场依据。 下面这段把特征重要性拉出来画图,跑完你能直接看到哪几个因子权重排前。get_feature_importance() 返回的数组顺序和训练列一致,sort_values 之后横排条形图最高项置顶,一眼定位主导变量。

MQL5 / C++
class="kw">import matplotlib.pyplot as plt
# Get feature importances
importances = model.get_feature_importance()
feature_names = X_train.columns if hasattr(X_train, &class="macro">#x27;columns&class="macro">#x27;) else [f&class="macro">#x27;feature_{i}&class="macro">#x27; for i in range(X_train.shape[class="num">1])]
# Create DataFrame for plotting
feat_imp_df = pd.DataFrame({
    &class="macro">#x27;Feature&class="macro">#x27;: feature_names,
    &class="macro">#x27;Importance&class="macro">#x27;: importances
}).sort_values(by=&class="macro">#x27;Importance&class="macro">#x27;, ascending=False)
# Plot
plt.figure(figsize=(class="num">7, class="num">3))
plt.barh(feat_imp_df[&class="macro">#x27;Feature&class="macro">#x27;], feat_imp_df[&class="macro">#x27;Importance&class="macro">#x27;])
plt.gca().invert_yaxis()  # Highest importance on top
plt.title(&class="macro">#x27;Feature Importances&class="macro">#x27;)
plt.xlabel(&class="macro">#x27;Importance&class="macro">#x27;)
plt.ylabel(&class="macro">#x27;Feature&class="macro">#x27;)
plt.tight_layout()
plt.show()

把烛条模型塞进EA跑两年回测

模型训完不等于能赚钱,得先转成 MT5 认得的 ONNX。下面这段 Python 负责把 Catboost 模型序列化,EA 侧再读进来跑推理。

MQL5 / C++
model_onnx = convert_sklearn(
    model,
    "catboost",
    [("input", FloatTensorType([None, X_train.shape[class="num">1]]))],
    target_opset={"": class="num">12, "ai.onnx.ml": class="num">2},
)
# And save.
with open(f"CatBoost.CDLPatterns.{symbol}.onnx", "wb") as f:
    f.write(model_onnx.SerializeToString())
class="macro">#include <Trade\Trade.mqh> class=class="str">"cmt">//The trading module
class="macro">#include <Trade\PositionInfo.mqh> class=class="str">"cmt">//Position handling module
class="macro">#include <ta-lib.mqh> class=class="str">"cmt">//For candlestick patterns
class="macro">#include <Catboost.mqh> class=class="str">"cmt">//Has a class for deploying a catboost model
CTrade m_trade;
CPositionInfo m_position;
CCatboostClassifier catboost;
input class="type">int magic_number = class="num">21042025;
input class="type">int slippage = class="num">100;
input class="type">class="kw">string symbol_ = "XAUUSD";
input ENUM_TIMEFRAMES timeframe_ = PERIOD_D1;
input class="type">int lookahead = class="num">1;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
   if (!MQLInfoInteger(MQL_TESTER))
   if (!ChartSetSymbolPeriod(class="num">0, symbol_, timeframe_))
     {
      printf("%s failed to set symbol %s and timeframe %s, Check these values. Err = %d",__FUNCTION__,symbol_,EnumToString(timeframe_),GetLastError());
      class="kw">return INIT_FAILED;
     }
class=class="str">"cmt">//---
   if (!catboost.Init(StringFormat("CatBoost.CDLPatterns.%s.onnx",symbol_), ONNX_COMMON_FOLDER)) class=class="str">"cmt">//Initialize the catboost model
      class="kw">return INIT_FAILED;
class=class="str">"cmt">//---
   m_trade.SetExpertMagicNumber(magic_number);
   m_trade.SetDeviationInPoints(slippage);
   m_trade.SetMarginMode();
   m_trade.SetTypeFillingBySymbol(Symbol());
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//---
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
代码里 OnInit 先切图表到 XAUUSD 日线,再从 Common 文件夹加载 CatBoost.CDLPatterns.XAUUSD.onnx;magic_number 设 21042025、lookahead=1 代表下一根收盘就平仓,没挂止损止盈。 第一版回测两年,多空几乎对称:空头 257 笔、多头 259 笔。问题出在阳线阴线每根都触发,相当于模型被最平庸的特征带偏,因为训练集里大量零值特征稀释了特殊形态的信号。 改法是只认除阳阴线外的独特烛条形态——所有特殊形态字段为 0 时禁止开仓。日线训练本就稀缺,改完交易数骤减,胜率 54.55%,和分类报告里 0.53 总体准确率贴近,说明这条路大概率没走歪。外汇贵金属杠杆高,回测吻合不等于实盘能复现,上 MT5 用策略测试器跑一遍自己的品种最踏实。

MQL5 / C++
model_onnx = convert_sklearn(
    model,
    "catboost",
    [("input", FloatTensorType([None, X_train.shape[class="num">1]]))],
    target_opset={"": class="num">12, "ai.onnx.ml": class="num">2},
)
# And save.
with open(f"CatBoost.CDLPatterns.{symbol}.onnx", "wb") as f:
    f.write(model_onnx.SerializeToString())
class="macro">#include <Trade\Trade.mqh> class=class="str">"cmt">//The trading module
class="macro">#include <Trade\PositionInfo.mqh> class=class="str">"cmt">//Position handling module
class="macro">#include <ta-lib.mqh> class=class="str">"cmt">//For candlestick patterns
class="macro">#include <Catboost.mqh> class=class="str">"cmt">//Has a class for deploying a catboost model
CTrade m_trade;
CPositionInfo m_position;
CCatboostClassifier catboost;
input class="type">int magic_number = class="num">21042025;
input class="type">int slippage = class="num">100;
input class="type">class="kw">string symbol_ = "XAUUSD";
input ENUM_TIMEFRAMES timeframe_ = PERIOD_D1;
input class="type">int lookahead = class="num">1;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
   if (!MQLInfoInteger(MQL_TESTER))
   if (!ChartSetSymbolPeriod(class="num">0, symbol_, timeframe_))
     {
      printf("%s failed to set symbol %s and timeframe %s, Check these values. Err = %d",__FUNCTION__,symbol_,EnumToString(timeframe_),GetLastError());
      class="kw">return INIT_FAILED;
     }
class=class="str">"cmt">//---
   if (!catboost.Init(StringFormat("CatBoost.CDLPatterns.%s.onnx",symbol_), ONNX_COMMON_FOLDER)) class=class="str">"cmt">//Initialize the catboost model
      class="kw">return INIT_FAILED;
class=class="str">"cmt">//---
   m_trade.SetExpertMagicNumber(magic_number);
   m_trade.SetDeviationInPoints(slippage);
   m_trade.SetMarginMode();
   m_trade.SetTypeFillingBySymbol(Symbol());
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//---
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
让小布替你跑这套形态扫描
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到实时烛条形态标注,把重复劳动交给小布,你专注决策。

常见问题

单根形态可在当前柱线完成检测,延迟低、易编码,适合作为机器学习特征;多根组合需回溯历史柱,工程复杂且易过拟合。
可以,小布盯盘的品种页已内置单根烛条形态识别,并标注阳线、阴线、十字星等类型,省去自行编写指标的环节。
TALib 覆盖多语言技术分析函数,CTALib 侧重以类似接口封装烛条形态检测,便于在 MQL5 中按柱线调用并返回形态枚举。
建议保留 OHLC、形态标签、所属品种与周期、时间戳;若做监督学习还需后续 N 根柱的走向标签作为目标变量。
需用样本外数据验证准确率,对比基线策略的胜率与回撤,并控制特征维度,防止模型记忆噪声而非通用形态规律。