构建K线趋势约束模型(第五部分):通知系统(第三部分)(基础篇)
📘

构建K线趋势约束模型(第五部分):通知系统(第三部分)(基础篇)

第 1/3 篇

「先把通知链路跑通再谈策略」

这套趋势约束模型的第五部分把重心放在通知系统:在 MT5 里跑出来的信号,如果不主动推送到手机,盯盘效率会塌一半。原文给出的落地路径是先配置消息 API,再用一个独立 Python 脚本把信号转成 WhatsApp 消息,最后回写到趋势约束指标里做触发测试。 从工程角度看,最关键的不是指标算得准不准,而是通知是否低延迟、可复现。作者给出的参考实现里,send_whatsapp_message.py 作为外部进程存在,MT5 侧只负责在条件满足时调用,这种解耦方式能避免终端卡死。 一个可验证的数据点是:该文发布于 2025 年 1 月 14 日,截至原文统计页面显示阅读量 657、评论 0——说明这类「MT5+外部消息网关」的硬核改造目前受众偏窄,但恰恰是资深交易者值得自己复刻的链路。外汇与贵金属品种波动剧烈、滑点风险高,任何自动通知都不能替代你本人的风控确认。

◍ 把 MT5 信号推到 WhatsApp 的初衷与流程骨架

这一节讲的是把自定义 MT5 指标产生的信号自动发到 WhatsApp 号码或群组的出发点。作者顺带提到 WhatsApp 新出的频道(Channels)功能,能让信号触达更广的受众;同样思路也适用于 Telegram 等平台,本质都是把社群渠道接进交易终端。 作者画了一张简化流程图,描述任意社交平台与 MT5 集成的分步开发路径。关键决策点只有一个:测试是否成功。成功就部署并进入监控维护,不正常就退回到开发环境重查步骤、改完再测;若发现平台没有可用 API,则回退去试别的社交平台。 需要留意的是,外汇与贵金属交易本身高风险,信号推送只是通知手段,不代表任何方向确定性。这套流程里作者明确说要做好反复排查错误的准备——集成调试往往不是一次过。下一节才会落地到 Telegram 与趋势约束指标的具体接法。

用 Twilio 打通 MT5 的 WhatsApp 推送

把 MT5 信号推到 WhatsApp,本质是把消息 API 接进 EA。和接 Telegram 类似,WhatsApp 官方与第三方都给了可用接口;选它的硬理由只有一个——用户盘子大。据 whatsthebigdata 统计,2023 年 7 月其全球月活约 27.8 亿,2020 年破 20 亿,2025 年预估 31.4 亿,覆盖面确实比其他 IM 广。 落地我用的是 Twilio:API 封装相对友好,WhatsApp 集成文档清楚,但要高级能力得订阅付费。同类还有 signalwire、heroku 等,多数提供试用,配置深浅不一,值得横向比一遍。 开通后关键四要素都在 Twilio 控制台:account_sid、auth_token、sandbox 分配的 WhatsApp 号码、你自己的接收号。注意 sandbox 模式只能发给「已加入」的号——先把 sandbox 号存联系人,发 join so-cave 完成订阅,才能在脚本里测通。外汇/贵金属信号走这种推送仅作监控辅助,杠杆品种波动剧烈,任何推送延迟都可能放大风险,参数请以账户实况为准。

「用 Python 脚本把信号推到 WhatsApp」

想让 MT5 报警或分析结论直接弹到手机,最轻量的办法是写个 Python 脚本调 Twilio 的 WhatsApp 接口。编辑器用 IDLE 或 Notepad++ 都行,脚本存到 Python 解释器目录下的 scripts 文件夹里,从那儿跑基本不会踩路径坑。 脚本开头只引两个模块:sys 负责读命令行参数,requests 负责发 HTTP 请求。核心函数 send_whatsapp_message 接收 account_sid、auth_token、收发双方 whatsapp: 前缀号码和消息文本,拼出 Twilio 的 Messages.json 端点并 POST 出去。 Twilio API 返回 201 才代表投递成功,其他状态码都会在命令行打印具体错误。实测中如果凭据填错,常见报错是 401 或 404,得回去核对 SID 和启用了 WhatsApp 的发送号码。 把脚本存成 send_whatsapp_message.py 后,在命令提示符进到该目录,执行 python send_whatsapp_message.py "Test Message" 即可。若窗口回显 Message sent successfully,说明链路通了,后续把 MT5 的 signal 文本喂给这个参数就能自动推送到你本人 WhatsApp。

MQL5 / C++
class="kw">import sys
class="kw">import requests
def send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message):
    url = f"https:class=class="str">"cmt">//api.twilio.com/class="num">2010-class="num">04-class="num">01/Accounts/{account_sid}/Messages.json"
    
    data = {
        "From": f"whatsapp:{from_whatsapp_number}",
        "To": f"whatsapp:{to_whatsapp_number}",
        "Body": message,
    }
    
    response = requests.post(url, data=data, auth=(account_sid, auth_token))
    
    if response.status_code == class="num">201:
        print("Message sent successfully")
    else:
        print(f"Failed to send message: {response.status_code} - {response.text}")
# Replace with your actual Twilio credentials and phone numbers
account_sid = "**********************" # Your SID
auth_token = "***************************" # Your Auth Token
from_whatsapp_number = "+class="num">14**********" # Your WhatsApp-enabled Twilio number
to_whatsapp_number = "+************" # Recipient&class="macro">#x27;s WhatsApp number
message = sys.argv[class="num">1]
send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message)

◍ 把信号推到 WhatsApp 而不动指标内核

想在 Trend Constraint V1.06 上接 WhatsApp,最省事的做法是只改 myAlert 块,不碰指标主逻辑。原 Telegram 那套调用结构基本能复用,只是把外发通道换成 Python 脚本,路径填错就完全发不出去。 OnInit() 里塞了一条启动欢迎语,指标一挂到图表就先发「订阅成功」到你的号码,用来确认通道在收真实信号前是通的。 冷却参数 alert_cooldown_seconds 默认 60 秒,意味着同一图表 1 分钟内重复触发只发一次,避免刷屏把 WhatsApp 接口打挂。 下面这段是改完的关键代码,黄色高亮处就是相对 Telegram 版真正动过的地方:python 解释器路径和脚本路径必须换成你本机真实地址,作者原样留的是 Python312 的示例目录。 [代码逐行拆解] OnInit(): 初始化时调用 myAlert("info", ...) 发测试信息,返回 INIT_SUCCEEDED 表示加载成功。 #import "shell32.dll" + ShellExecuteW: 声明 Windows API,用来以系统权限拉起外部进程。 last_alert_time / alert_cooldown_seconds: 全局时间戳与冷却秒数,控制告警频率。 myAlert(): 先取当前时间,若距上次告警不足 60 秒直接 return;否则更新时间戳并拼 full_message(含品种、周期、内容)。

分支 type=="indicator""info": 在原弹窗/推送之后,新增拼接 python.exe 及 send_whatsapp_message.py 的命令串,并用 Print 输出便于排查。
MQL5 / C++
class="type">int OnInit()
{
class=class="str">"cmt">// Send test message on launch
   myAlert("info", "Thank you for subscribing. You shall be receiving Trend Constraint signal alerts via Whatsapp.");
   
class="kw">return(INIT_SUCCEEDED);
}
class=class="str">"cmt">//--- ShellExecuteW declaration ----------------------------------------------
class="macro">#class="kw">import "shell32.dll"
class="type">int ShellExecuteW(class="type">int hwnd, class="type">class="kw">string lpOperation, class="type">class="kw">string lpFile, class="type">class="kw">string lpParameters, class="type">class="kw">string lpDirectory, class="type">int nShowCmd);
class="macro">#class="kw">import
class=class="str">"cmt">//--- global variables ------------------------------------------------------
class="type">class="kw">datetime last_alert_time;
input class="type">int alert_cooldown_seconds = class="num">60; class=class="str">"cmt">// Cooldown period in seconds
class=class="str">"cmt">//--- myAlert function ------------------------------------------------------
class="type">void myAlert(class="type">class="kw">string type, class="type">class="kw">string message) {
    class="type">class="kw">datetime current_time = TimeCurrent();
    if (current_time - last_alert_time < alert_cooldown_seconds) {
        class=class="str">"cmt">// Skip alert if within cooldown period
        class="kw">return;
    }
    last_alert_time = current_time;
    class="type">class="kw">string full_message = type + " | Trend Constraint V1.class="num">06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message;
    if (type == "print") {
        Print(message);
    } else if (type == "error") {
        Print(type + " | Trend Constraint V1.class="num">06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message);
    } else if (type == "order") {
        class=class="str">"cmt">// Add order alert handling if needed
    } else if (type == "modify") {
        class=class="str">"cmt">// Add modify alert handling if needed
    } else if (type == "indicator" || type == "info") {
        if (Audible_Alerts) {
            Alert(full_message);
        }
        if (Push_Notifications) {
            SendNotification(full_message);
        }
        class=class="str">"cmt">// Send to WhatsApp //Edit to match your actual path, these I gave as an example for my computer
        class="type">class="kw">string python_path = "C:\\Users\\******\\AppData\\Local\\Programs\\Python\\Python312\\python.exe";
        class="type">class="kw">string script_path = "C:\\Users\\******\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_whatsapp_message.py";
        class="type">class="kw">string command = python_path + " \"" + script_path + "\" \"" + full_message + "\"";
        
        class=class="str">"cmt">// Debugging: Print the command being executed
        Print("Executing command to send WhatsApp message: ", command);

用 cmd 桥接外部脚本与日线趋势信号雏形

在 EA 或脚本里调起外部程序,常见做法是拼一条 cmd 命令并留几秒等待窗口。下面这段代码把待执行指令包成 /c 参数,追加 && timeout 5 强制等待 5 秒,再交给 ShellExecuteW 以窗口模式拉起 cmd.exe。 返回值若 ≤32 视为失败,此时用 GetLastError 抓错误码并打印;大于 32 则视为拉起成功,把 result 作为结果码输出。这套机制能让 MT5 在不动主线程的情况下,把 Python 训练或推演任务甩给系统 shell 跑。 顺着这个思路,有一版名为 Trend Constraint V1.06 的指标只做一件事:D1 蜡烛收阳才允许出买信号,收阴才允许出卖信号。它挂 6 个 buffer、6 个 plot,买箭头宽 5 像素、色值 0xFF3C00,卖箭头同宽、色值 0x0000FF,另留两组宽 2 的反转箭头与一条宽 2 的实线缓冲。 把日线方向当硬约束,能砍掉一大批逆势噪音信号;但外汇与贵金属杠杆高、滑点跳空频繁,这种过滤只降低频率不保证胜率,实盘前务必在 MT5 策略测试器里用历史数据回放验证。

MQL5 / C++
class=class="str">"cmt">// Use cmd.exe to execute the command and then wait for class="num">5 seconds
class="type">class="kw">string final_command = "/c " + command + " && timeout class="num">5";
class="type">int result = ShellExecuteW(class="num">0, "open", "cmd.exe", final_command, NULL, class="num">1);
if (result <= class="num">32) {
    class="type">int error_code = GetLastError();
    Print("Failed to execute Python script. Error code: ", error_code);
} else {
    Print("Successfully executed Python script. Result code: ", result);
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                     Trend Constraint V1.class="num">06.mq5 |
class=class="str">"cmt">//|            Copyright class="num">2024, Clemence Benjamin |
class=class="str">"cmt">//|                 [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2024, Clemence Benjamin"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.06"
class="macro">#class="kw">property description "A model that seeks to produce sell signals when D1 candle is Bearish only and buy signals when it is Bullish"
class=class="str">"cmt">//--- indicator settings
class="macro">#class="kw">property indicator_chart_window
class="macro">#class="kw">property indicator_buffers class="num">6
class="macro">#class="kw">property indicator_plots class="num">6
class="macro">#class="kw">property indicator_type1 DRAW_ARROW
class="macro">#class="kw">property indicator_width1 class="num">5
class="macro">#class="kw">property indicator_color1 0xFF3C00
class="macro">#class="kw">property indicator_label1 "Buy"
class="macro">#class="kw">property indicator_type2 DRAW_ARROW
class="macro">#class="kw">property indicator_width2 class="num">5
class="macro">#class="kw">property indicator_color2 0x0000FF
class="macro">#class="kw">property indicator_label2 "Sell"
class="macro">#class="kw">property indicator_type3 DRAW_ARROW
class="macro">#class="kw">property indicator_width3 class="num">2
class="macro">#class="kw">property indicator_color3 0xE8351A
class="macro">#class="kw">property indicator_label3 "Buy Reversal"
class="macro">#class="kw">property indicator_type4 DRAW_ARROW
class="macro">#class="kw">property indicator_width4 class="num">2
class="macro">#class="kw">property indicator_color4 0x1A1AE8
class="macro">#class="kw">property indicator_label4 "Sell Reversal"
class="macro">#class="kw">property indicator_type5 DRAW_LINE
class="macro">#class="kw">property indicator_style5 STYLE_SOLID
class="macro">#class="kw">property indicator_width5 class="num">2

常见问题

可用外部脚本桥接,让终端在出信号时调用系统命令把消息发到聊天软件,先跑通链路再接策略。
需要Twilio账号、API凭据和一段转发脚本,终端触发时把信号文本交给脚本推送即可。
小布可接管这类重复通知劳动,你只需在品种页配置接收方式,它按规则把信号推到你指定的聊天端。
保持指标内核不动,用外部命令在信号发生时调用独立脚本发送,解耦后维护更省事。
先确认信号能可靠到达,才能避免策略跑起来却漏看,通知链路是验证闭环的第一步。