交易报告及短信通知的创建和发布·综合运用
📡

交易报告及短信通知的创建和发布·综合运用

(3/3)·从HTML报告生成、FTP发布到短信网关,一篇把前三篇零散模块拼成可运行的发布流水线

案例拆解 第 3/3 篇
很多人写完EA只盯终端日志,实盘跑一周都懒得回头翻历史。没有外部报告,账号异动只能靠人肉刷屏,漏掉关键信号是早晚的事。把结果自动推出去,才是能长期跑系统的前提。

◍ 用脚本自动截平衡曲线并回传

做 EA 或脚本复盘时,手动存资金曲线太慢。下面这段逻辑直接在离线图表上关掉平移、开自动滚动,写个注释后重绘,再停 8000 毫秒等渲染稳定,随后用 ChartScreenShot 把平衡图存成 picture1.gif。 截图宽高按时间跨度算:(EndTime-StartTime)/PeriodSeconds(Picture1_period) 作为宽,一半作为高,右对齐。对象清掉、图表关掉,若终端开了 FTP,就把 report.html 加两张 gif 一并 SendFTP 传走。 外汇和贵金属品种波动大,自动回传的报告只是历史快照,不代表后续资金曲线倾向重复,实盘前请在 MT5 策略测试器里用自己的品种跑一遍验证路径和权限。 别把脚本当监控守护 这段代码跑完就 ChartClose 了,不适合常驻看盘;要持续盯平衡曲线得另起定时器或指标,否则图关了你也收不到新截图。

MQL5 / C++
ChartSetInteger(hChart,CHART_SHIFT,false);                 class=class="str">"cmt">// 没有图表转换
ChartSetInteger(hChart,CHART_AUTOSCROLL,true);                class=class="str">"cmt">// 启用自动滚动
ChartSetString(hChart,CHART_COMMENT,"BALANCE");              class=class="str">"cmt">// 图表的注释
class=class="str">"cmt">// 重绘平衡图表
ChartRedraw(hChart);
Sleep(class="num">8000);
class=class="str">"cmt">// 平衡图表截图
ChartScreenShot(hChart,"picture1.gif",(class="type">int)(EndTime-StartTime)/PeriodSeconds(Picture1_period),
(class="type">int)(EndTime-StartTime)/PeriodSeconds(Picture1_period)/class="num">2,ALIGN_RIGHT);
class=class="str">"cmt">// 删除平衡图表的所有对象
ObjectsDeleteAll(hChart);
class=class="str">"cmt">// 关闭图表
ChartClose(hChart);
class=class="str">"cmt">// 如果启用了发布报告 - 通过 FTP 发送
class=class="str">"cmt">// HTML-文件和两个图片 - 价格图表和平衡图表
if(TerminalInfoInteger(TERMINAL_FTP_ENABLED))
{
SendFTP("report.html");
SendFTP("picture1.gif");
SendFTP("picture2.gif");
}

「用邮件网关把 MT5 提醒推到手机短信」

人离开电脑只带手机时,靠运营商的邮件转短信网关还能盯盘。前提是你在 MT5 里配好 Email 选项卡:Tools→Options→Email,勾 Enable 填 SMTP、登录密码和收件人地址(即运营商给的短信邮箱),点 Test 能在日志看到验证信发出即通。 不想写代码的话,Toolbox 里右键 Create 建提醒,动作选 Mail,设品种、条件、触发价,Maximum iterations 填 1 就不会重复轰炸。价格触线就可能收到短信。 程序里用 SendMail() 更灵活,两个参数分别是标题和正文。可挂在 OrderSend() 之后报成交,也可放进 OnTimer() 周期播报报价,或等指标交叉、价格碰线时触发。外汇和贵金属波动剧烈,短信到达有延迟,只能当辅助监控不能替风控。 下面这段把 OrderSend 结果拼成中文短信,开 MT5 建 EA 直接粘能跑:

MQL5 / C++
<span class="functions">OrderSend</span>(request,result};
<span class="keyword">class="type">class="kw">string</span> msg_subj,msg_text;
<span class="keyword">if</span>(<span class="functions">OrderSend</span>(request,result))
{
<span class="keyword">class="kw">switch</span>(request.action)
{
<span class="keyword">case</span> <span class="keyword">TRADE_ACTION_DEAL</span>:
<span class="keyword">class="kw">switch</span>(request.type)
{
<span class="keyword">case</span> <span class="keyword">ORDER_TYPE_BUY</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"Buy "</span>,result.volume,<span class="class="type">class="kw">string">" "</span>,request.symbol,<span class="class="type">class="kw">string">" 价位 "</span>,result.price,<span class="class="type">class="kw">string">", 止损="</span>,request.sl,<span class="class="type">class="kw">string">", 获利="</span>,request.tp);
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">ORDER_TYPE_SELL</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"Sell "</span>,result.volume,<span class="class="type">class="kw">string">" "</span>,request.symbol,<span class="class="type">class="kw">string">" 价位 "</span>,result.price,<span class="class="type">class="kw">string">", 止损="</span>,request.sl,<span class="class="type">class="kw">string">", 获利="</span>,request.tp);
<span class="keyword">break</span>;
}
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">TRADE_ACTION_PENDING</span>:
<span class="keyword">class="kw">switch</span>(request.type)
{
<span class="keyword">case</span> <span class="keyword">ORDER_TYPE_BUY_LIMIT</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"设置BuyLimit "</span>,result.volume,<span class="class="type">class="kw">string">" "</span>,request.symbol,<span class="class="type">class="kw">string">" 价位 "</span>,request.price,<span class="class="type">class="kw">string">", 止损="</span>,request.sl,<span class="class="type">class="kw">string">", 获利="</span>,request.tp);
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">ORDER_TYPE_SELL_LIMIT</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"设置 SellLimit "</span>,result.volume,<span class="class="type">class="kw">string">" "</span>,request.symbol,<span class="class="type">class="kw">string">" 价位 "</span>,request.price,<span class="class="type">class="kw">string">", 止损="</span>,request.sl,<span class="class="type">class="kw">string">", 获利="</span>,request.tp);
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">ORDER_TYPE_BUY_STOP</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"设置 BuyStop "</span>,result.volume,<span class="class="type">class="kw">string">" "</span>,request.symbol,<span class="class="type">class="kw">string">" 价位 "</span>,request.price,<span class="class="type">class="kw">string">", 止损="</span>,request.sl,<span class="class="type">class="kw">string">", 获利="</span>,request.tp);
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">ORDER_TYPE_SELL_STOP</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"设置 SellStop "</span>,result.volume,<span class="class="type">class="kw">string">" "</span>,request.symbol,<span class="class="type">class="kw">string">" 价位 "</span>,request.price,<span class="class="type">class="kw">string">", 止损="</span>,request.sl,<span class="class="type">class="kw">string">", 获利="</span>,request.tp);
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">ORDER_TYPE_BUY_STOP_LIMIT</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"设置 BuyStopLimit "</span>,result.volume,<span class="class="type">class="kw">string">" "</span>,request.symbol,<span class="class="type">class="kw">string">" 价位 "</span>,request.price,<span class="class="type">class="kw">string">", stoplimit="</span>,request.stoplimit,
<span class="class="type">class="kw">string">               ", 止损="</span>,request.sl,<span class="class="type">class="kw">string">", 获利="</span>,request.tp);
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">ORDER_TYPE_SELL_STOP_LIMIT</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"设置 SellStop "</span>,result.volume,<span class="class="type">class="kw">string">" "</span>,request.symbol,<span class="class="type">class="kw">string">" 价位 "</span>,request.price,<span class="class="type">class="kw">string">", stoplimit="</span>,request.stoplimit,<span class="class="type">class="kw">string">
", 止损="</span>,request.sl,<span class="class="type">class="kw">string">", 获利="</span>,request.tp);
<span class="keyword">break</span>;
}
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">TRADE_ACTION_SLTP</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"修改止损和获利. SL="</span>,request.sl,<span class="class="type">class="kw">string">", TP="</span>,request.tp);
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">TRADE_ACTION_MODIFY</span>:
<span class="functions">StringConcatenate</span>(msg_text,<span class="class="type">class="kw">string">"修改订单"</span>,result.price,<span class="class="type">class="kw">string">", 止损="</span>,request.sl,<span class="class="type">class="kw">string">", 获利="</span>,request.tp);
<span class="keyword">break</span>;
<span class="keyword">case</span> <span class="keyword">TRADE_ACTION_REMOVE</span>:

用邮件把挂单异常推到手机

EA 在删除挂单失败时,会把 msg_text 置为「错误!」,成功则写「删除订单」并 break 退出循环。随后用 StringConcatenate 拼出邮件主题,格式为「账号-经纪商」,例如 59181-MetaQuotes Software Corp.,再调用 SendMail 把结果发到绑定邮箱,MT5 手机端能直接收提醒。 下面这段是定时播报当前价的骨架:OnInit 里 EventSetTimer(3600) 设每小时触发一次,OnDeinit 里 EventKillTimer 清掉定时器避免残留,OnTimer 每次把 Symbol() 的 BID 价用 DoubleToString 按 _Digits 精度转成字符串发出去。 [CODE] void OnInit() { EventSetTimer(3600); } void OnDeinit(const int reason) { EventKillTimer(); } void OnTimer() { SendMail(Symbol(),DoubleToString(SymbolInfoDouble(Symbol(),SYMBOL_BID),_Digits)); } [/CODE] 实盘接邮件通知前,先在策略测试器里跑一晚,确认 3600 秒周期下 MT5 的邮件配置没被券商限流。外汇和贵金属波动剧烈,邮件延迟几秒就可能错过出场点,这类提醒只适合辅助盯盘,不能当风控主力。

MQL5 / C++
msg_text="删除订单";
break;
}
}
else msg_text="错误!";
StringConcatenate(msg_subj,AccountInfoInteger(ACCOUNT_LOGIN),"-",AccountInfoString(ACCOUNT_COMPANY));
SendMail(msg_subj,msg_text);
class="num">59181-MetaQuotes Software Corp. Buy class="num">0.1 EURUSD at price class="num">1.23809, SL=class="num">1.2345, TP=class="num">1.2415
class="type">void OnInit()
{
EventSetTimer(class="num">3600);
}
class="type">void OnDeinit(const class="type">int reason)
{
EventKillTimer();
}
class="type">void OnTimer()
{
SendMail(Symbol(),DoubleToString(SymbolInfoDouble(Symbol(),SYMBOL_BID),_Digits));
}

◍ 画得少,看得清

把交易报告自动生成并推到手机或网页,本质上只做三件事:用 MQL5 写文件、用 FTP 传出去、用通知补一刀。report.zip 里那个 52.91 KB 的 sendreport_en.mq5 就是现成骨架,改两处参数就能跑通一套自己的日报。 外汇和贵金属杠杆高、滑点狠,自动发报告只是降低漏看的概率,不替你判断该不该进场。真要验证,开 MT5 把脚本挂上历史品种,看 HTML 里那张权益曲线和成交分布是否对得上终端。 留个念头:下次你嫌 MT5 自带报表太挤,就直接用这套思路砍掉冗余列,只留手数、点位、盈亏三栏——画面干净了,毛病反而一眼能抓。

让小布替你收这些推送
小布盯盘已内置品种页的异动摘要与事件流,你不必自己搭FTP和短信网关,打开对应品种页就能看到浓缩后的交易状态,把搭基础设施的重复劳动交给小布,你专注决策。

常见问题

除report.html、picture1.gif和picture2.gif外,还需buy.gif与sell.gif两个箭头图,放在WWW服务器与报告同目录,否则买卖标记可能空白。
可同时配置第三方短信网关作为备用,或在EA里加发送结果校验,失败写本地日志,概率上能降低漏通知风险,外汇贵金属本身波动大更该留痕。
小布盯盘不替换你的EA发布逻辑,但提供现成的品种事件流与摘要视图,若你只想要随时看状态而不想维护服务器,用内置页更轻量。
脚本以用户输入的起止时间为准,结束取当前时间,选昨天即起止为昨零到昨末,平衡图横轴按该区间压缩,超出部分不绘。