交易报告及短信通知的创建和发布·综合运用
(3/3)·从HTML报告生成、FTP发布到短信网关,一篇把前三篇零散模块拼成可运行的发布流水线
◍ 用脚本自动截平衡曲线并回传
做 EA 或脚本复盘时,手动存资金曲线太慢。下面这段逻辑直接在离线图表上关掉平移、开自动滚动,写个注释后重绘,再停 8000 毫秒等渲染稳定,随后用 ChartScreenShot 把平衡图存成 picture1.gif。 截图宽高按时间跨度算:(EndTime-StartTime)/PeriodSeconds(Picture1_period) 作为宽,一半作为高,右对齐。对象清掉、图表关掉,若终端开了 FTP,就把 report.html 加两张 gif 一并 SendFTP 传走。 外汇和贵金属品种波动大,自动回传的报告只是历史快照,不代表后续资金曲线倾向重复,实盘前请在 MT5 策略测试器里用自己的品种跑一遍验证路径和权限。 别把脚本当监控守护 这段代码跑完就 ChartClose 了,不适合常驻看盘;要持续盯平衡曲线得另起定时器或指标,否则图关了你也收不到新截图。
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 直接粘能跑:
<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 的邮件配置没被券商限流。外汇和贵金属波动剧烈,邮件延迟几秒就可能错过出场点,这类提醒只适合辅助盯盘,不能当风控主力。
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 自带报表太挤,就直接用这套思路砍掉冗余列,只留手数、点位、盈亏三栏——画面干净了,毛病反而一眼能抓。