构建K线图趋势约束模型(第六部分):一体化集成(基础篇)
◍ 把趋势约束模型塞进EA主循环
原文给出的系列第六部分定位是「一体化集成」,目标只有一个:把前面拆开的 K 线图趋势约束模型,作为一个模块接进 MT5 的 EA 主逻辑里,而不是孤立脚本。MetaTrader 5 上这类集成若没跑通事件钩子,模型算得再准也不会在 tick 里生效。 集成的关键动作是统一调用入口:在 OnTick 或自定义定时器里触发约束判定,再把结果喂给下单前置条件。原文在 2025 年 1 月 29 日发布该节,页面基础互动数据为 713 次浏览、2 条评论,说明这套接法在社区里还偏冷门,值得自己复刻验证。 评论功能本身也被纳入集成范围——把模型输出的趋势偏离度直接写成图表注释或日志,交易者在盯盘时能肉眼核对约束是否生效。外汇与贵金属品种波动跳空频繁,这类可视化回读对排查误触发很有用,但杠杆品种高风险,模型仅作概率过滤、不构成方向保证。
「两套通知程序的合并思路」
现有信号系统经历过多个迭代,趋势约束指标的前两版各自在第五部分里有独立的集成实现。现在要把逻辑相近但功能不同的两套程序并成一个统一信号系统,而不是简单把源码复制粘贴。 我习惯把这一步叫“合并”:保留两边效果一致的核心片段,丢掉冗余,让 MT5 在后台以极高速度跑重复任务,把人从手工盯盘中解放出来。合并时要始终盯着全局变量和关键调用行,否则多段代码拼起来会互相踩内存。 从第五部分可以归纳出两条主集成路径:Telegram 接 MT5 做信号推送,以及 WhatsApp 接 MT5 做信号推送。两者都通过命令行隐藏窗口执行,避免弹窗干扰桌面,但也带来一个麻烦——窗口看不见,就无法确认信号到底发出去没有。 一个务实的改法是:每次广播成功,就在图表上用 Comment() 写一行标注,或者至少 Print() 进平台日志。外汇与贵金属波动剧烈、滑点频发,这类回执能帮你快速发现推送失败,而不用等客户来问。
把 WhatsApp 与 Telegram 塞进同一个 EA
做 MT5 信号转发最怕重复造轮子。把 WhatsApp 和 Telegram 两段独立脚本并成一个 myAlert,核心动作只有四件:顶部统一定义 ShellExecuteW 和冷却变量、扩展 myAlert 同时调两个外部 Python、命令执行逻辑写进同一函数、保留冷却避免刷屏。 合并后全局里 last_alert_time 记上次发报时间戳,alert_cooldown_seconds 是用户输入项,默认 60 秒。myAlert 开头先用 TimeCurrent()-last_alert_time < alert_cooldown_seconds 判断,未过冷却直接 return,这条不写稳,实盘可能一分钟被外部 API 限流封号。 ShellExecuteW 来自 shell32.dll,负责拉起 Python 发消息脚本。WhatsApp 走 Twilio 沙盒时有个坑:提供的测试沙盒连接 72 小时内过期,重连需发独特指令如“join so-cave”,要永久得买 Twilio 号码。 实测编译后从 MT5 安卓推送、Telegram、WhatsApp 三端都收到测试信号,指标由 V1.06 升到 V1.07 无报错。外汇与贵金属行情波动剧烈,外接消息通道仅作辅助提醒,任何信号都不构成入场保证,实盘前请在策略测试器跑通再挂真账户。
/--- ShellExecuteW declaration ---------------------------------------------- #<span class="preprocessor">class="kw">import </span><span class="class="type">class="kw">string">"shell32.dll"</span> <span class="keyword">class="type">int</span> ShellExecuteW(<span class="keyword">class="type">int</span> hwnd, <span class="keyword">class="type">class="kw">string</span> lpOperation, <span class="keyword">class="type">class="kw">string</span> lpFile, <span class="keyword">class="type">class="kw">string</span> lpParameters, <span class="keyword">class="type">class="kw">string</span> lpDirectory, <span class="keyword">class="type">int</span> nShowCmd); <span class="preprocessor">class="macro">#class="kw">import </span><span class="comment">class=class="str">"cmt">//--- functions for telegram integration -----------------------------------------------</span> <span class="keyword">class="type">class="kw">datetime</span> last_alert_time; <span class="keyword">input</span> <span class="keyword">class="type">int</span> alert_cooldown_seconds = <span class="number">class="num">60</span>; <span class="comment">class=class="str">"cmt">// Cooldown period in seconds</span> <span class="keyword">class="type">void</span> myAlert(<span class="keyword">class="type">class="kw">string</span> type, <span class="keyword">class="type">class="kw">string</span> message) { <span class="keyword">class="type">class="kw">datetime</span> current_time = <span class="functions">TimeCurrent</span>(); <span class="keyword">if</span> (current_time - last_alert_time < alert_cooldown_seconds) { <span class="comment">class=class="str">"cmt">// Skip alert if within cooldown period</span> <span class="keyword">class="kw">return</span>; } last_alert_time = current_time; <span class="keyword">class="type">class="kw">string</span> full_message = type + <span class="class="type">class="kw">string">" | Trend Constraint V1.class="num">05 @ "</span> + <span class="functions">Symbol</span>() + <span class="class="type">class="kw">string">","</span> + <span class="functions">IntegerToString</span>(<span class="functions">Period</span>()) + <span class="class="type">class="kw">string">" | "</span> + message; <span class="keyword">if</span> (type == <span class="class="type">class="kw">string">"print"</span>) { <span class="functions">Print</span>(message); } <span class="keyword">else</span> <span class="keyword">if</span> (type == <span class="class="type">class="kw">string">"error"</span>) { <span class="functions">Print</span>(type + <span class="class="type">class="kw">string">" | Trend Constraint V1.class="num">05 @ "</span> + <span class="functions">Symbol</span>() + <span class="class="type">class="kw">string">","</span> + <span class="functions">IntegerToString</span>(<span class="functions">Period</span>()) + <span class="class="type">class="kw">string">" | "</span> + message); } <span class="keyword">else</span> <span class="keyword">if</span> (type == <span class="class="type">class="kw">string">"order"</span>) { <span class="comment">class=class="str">"cmt">// Add order alert handling if needed</span> } <span class="keyword">else</span> <span class="keyword">if</span> (type == <span class="class="type">class="kw">string">"modify"</span>) { <span class="comment">class=class="str">"cmt">// Add modify alert handling if needed</span> } <span class="keyword">else</span> <span class="keyword">if</span> (type == <span class="class="type">class="kw">string">"indicator"</span>) { <span class="keyword">if</span> (Audible_Alerts) { <span class="functions">Alert</span>(full_message); } <span class="keyword">if</span> (Push_Notifications) { <span class="functions">SendNotification</span>(full_message); } <span class="comment">class=class="str">"cmt">// Send to Telegram</span> <span class="keyword">class="type">class="kw">string</span> python_path = <span class="class="type">class="kw">string">"C:\\Users\\your_computer_name\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"</span>;
◍ MT5 调起 Python 推 Telegram 与警报冷却
在 MT5 的 MQL5 脚本里直接调起外部 Python 发 Telegram,核心是用 ShellExecuteW 走 cmd.exe 中转。下面这段把 Python 解释器路径和脚本路径拼成命令,并加 timeout 5 让窗口留 5 秒便于观察,返回码 ≤32 视为失败并打印 GetLastError 码。 string script_path = "C:\\Users\\your_computer_name\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_telegram_message.py"; string command = python_path + " \"" + script_path + "\" \"" + full_message + "\""; // Debugging: Print the command being executed Print("Executing command to send Telegram message: ", command); // Use cmd.exe to execute the command and then wait for 5 seconds string final_command = "/c " + command + " && timeout 5"; int result = ShellExecuteW(0, "open", "cmd.exe", final_command, NULL, 1); if (result <= 32) { int error_code = GetLastError(); Print("Failed to execute Python script. Error code: ", error_code); } else { Print("Successfully executed Python script. Result code: ", result); } } //--- ShellExecuteW declaration ---------------------------------------------- #import "shell32.dll" int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); #import //--- global variables ------------------------------------------------------ datetime last_alert_time; input int alert_cooldown_seconds = 60; // Cooldown period in seconds //--- myAlert function ------------------------------------------------------ void myAlert(string type, string message) { datetime current_time = TimeCurrent(); if (current_time - last_alert_time < alert_cooldown_seconds) { // Skip alert if within cooldown period return; } last_alert_time = current_time;
| string full_message = type + " | Trend Constraint V1.06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message; |
|---|
if (type == "print") { Print(message); } else if (type == "error") {
| Print(type + " | Trend Constraint V1.06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message); |
|---|
} else if (type == "order") { // Add order alert handling if needed } else if (type == "modify") { // Add modify alert handling if needed
| } else if (type == "indicator" | type == "info") { |
|---|
if (Audible_Alerts) { Alert(full_message); } 警报轰炸是实盘常见坑。myAlert 用 last_alert_time 配合 alert_cooldown_seconds(默认 60 秒)做节流,同周期内重复信号直接 return,避免 1 分钟里被弹窗刷屏。把冷却调到 300 可能在震荡市漏掉二次确认,调成 30 则更灵敏但易扰。 ShellExecuteW 必须#import "shell32.dll" 声明,nShowCmd 传 1 是正常激活窗口。若你机器 Python 装在 Python311 或其他盘,script_path 不改正就会返回 2(文件找不到),开 MT5 终端看 Experts 标签的 Print 输出即可定位。外汇与贵金属波动剧烈,这类自动推送仅作辅助提醒,信号误发概率始终存在。
class="type">class="kw">string script_path = "C:\\Users\\your_computer_name\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_telegram_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 Telegram message: ", command); 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">//--- 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); }
「把信号推到手机与 WhatsApp 的实跑写法」
EA 在触发信号后,除了 MT5 自带推送,还可以借 Windows 的 ShellExecuteW 拉起本地 Python 把消息发到 WhatsApp。下面这段是通知分支的核心:先判断 Push_Notifications 开关,过了就调 SendNotification 发 MT5 推送,随后拼出 python.exe 与脚本路径执行外部命令。
代码里 python_path 与 script_path 写死为 Python312 的安装目录,实盘前必须改成你本机真实路径,否则 ShellExecuteW 返回码会小于等于 32 并在日志报 GetLastError 错误码。final_command 用 /c 走 cmd.exe,末尾 && timeout 5 是故意留 5 秒让脚本跑完,避免 EA 主线程过早继续。
通知频率靠全局变量 alert_cooldown_seconds 卡死,默认 60 秒。myAlert 函数每次进来用 TimeCurrent 减 last_alert_time,差值不足 60 就直接 return,这意味着同一品种同周期 1 分钟内最多响一次,能压住震荡市的重复刷屏。
外汇与贵金属杠杆高、滑点大,这类外部消息通道只作辅助提醒,不能替代风控与人工确认,信号延迟可能导致错过或误判入场。
if (Push_Notifications) { SendNotification(full_message); } class=class="str">"cmt">// Send to WhatsApp class="type">class="kw">string python_path = "C:\\Users\\Your_Computer_Name\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"; class="type">class="kw">string script_path = "C:\\Users\\Your_Computer_Name\\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); 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">//--- 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") {
把指标警报推到 WhatsApp 和 Telegram
在 MT5 的 EA 或脚本里,当信号类型属于 indicator 或 info 时,除了本地弹窗和手机 Push,还可以借 Python 把消息甩到 WhatsApp 与 Telegram。核心思路是用 ShellExecuteW 调起 cmd.exe,再跑本地 Python 脚本,路径和参数拼成一条命令即可。 下面这段是实际可用的调用骨架:先写死 python.exe 与脚本的绝对路径,把 full_message 作为命令行参数传入,最后用 /c 让 cmd 执行并 timeout 5 留足进程退出时间。ShellExecuteW 返回值 ≤32 代表 shell 调用失败,这时用 GetLastError 抓具体错误码。 调试时别省 Print:把最终拼出的 whatsapp_command 和 telegram_command 原样打到日志,能直接复制去命令行手动跑,验证是 MT5 权限问题还是 Python 环境没配对。外汇与贵金属行情瞬息万变,这类外推通道只作辅助提醒,任何信号都有漏发或延迟可能,实盘前务必在模拟盘跑通整条链路。
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 class="type">class="kw">string python_path = "C:\\Users\\Your_Computer_Name\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"; class="type">class="kw">string whatsapp_script_path = "C:\\Users\\Your_Computer_Name\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_whatsapp_message.py"; class="type">class="kw">string whatsapp_command = python_path + " \"" + whatsapp_script_path + "\" \"" + full_message + "\""; class=class="str">"cmt">// Debugging: Print the command being executed for WhatsApp Print("Executing command to send WhatsApp message: ", whatsapp_command); 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_whatsapp_command = "/c " + whatsapp_command + " && timeout class="num">5"; class="type">int whatsapp_result = ShellExecuteW(class="num">0, "open", "cmd.exe", final_whatsapp_command, NULL, class="num">1); if (whatsapp_result <= class="num">32) { class="type">int error_code = GetLastError(); Print("Failed to execute WhatsApp Python script. Error code: ", error_code); } else { Print("Successfully executed WhatsApp Python script. Result code: ", whatsapp_result); } class=class="str">"cmt">// Send to Telegram class="type">class="kw">string telegram_script_path = "C:\\Users\\Your_Computer_Name\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_telegram_message.py"; class="type">class="kw">string telegram_command = python_path + " \"" + telegram_script_path + "\" \"" + full_message + "\""; class=class="str">"cmt">// Debugging: Print the command being executed for Telegram Print("Executing command to send Telegram message: ", telegram_command); 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_telegram_command = "/c " + telegram_command + " && timeout class="num">5"; class="type">int telegram_result = ShellExecuteW(class="num">0, "open", "cmd.exe", final_telegram_command, NULL, class="num">1); if (telegram_result <= class="num">32) { class="type">int error_code = GetLastError(); Print("Failed to execute Telegram Python script. Error code: ", error_code); } else {