如何使用 MetaTrader 和 Google Sheets 创建交易日志·综合运用
📊

如何使用 MetaTrader 和 Google Sheets 创建交易日志·综合运用

(3/3)· 把订单流自动写进表格并回读分析,告别手工记账的时间损耗与录入偏差

含代码示例 第 3/3 篇
手工抄写成交记录的交易者,常在收盘后漏记滑点和隔夜费,月底复盘才发现样本早已失真。用 MT5 直连表格却不懂回读机制,日志只是单向倾倒,分析仍靠肉眼。本篇把发布与读取两条链路一次打通。

◍ 交易记录 JSON 里的字段排列

上面这段是某笔订单在表格数据源里的原始 JSON 片段,用 c 数组承载一列列单元格值,顺序固定为: ticket、类型、品种、开仓价、止损价、止盈价、浮动盈亏…… 以 USDJPY 那笔为例,v 数值分别为 147.22、147.44、146.0,对应开仓卖单挂在 147.22,止损 147.44,止盈 146.0,距离开仓 122 点和 222 点。外汇与贵金属杠杆高,这类止损止盈间距若碰上央行讲话可能瞬间被打穿。 前面那一串 null 是预留的扩展列(如手续费、魔术码、注释),实际渲染时会被前端忽略;最后那个 {"v": null} 通常表示该行已平仓但盈亏字段尚未回写。打开 MT5 的自定义报表导出,对照这种结构能快速定位哪一段是订单主体、哪一段是垃圾填充。

MQL5 / C++
{
  "c": [
    {"v": class="num">123123.0, "f": "class="num">123123"},
    {"v": "sell"},
    {"v": "USDJPY"},
    {"v": class="num">147.22, "f": "class="num">147.22"},
    {"v": class="num">147.44, "f": "class="num">147.44"},
    {"v": class="num">146.0, "f": "class="num">146"},
    {"v": class="num">0.0, "f": "class="num">0"}
  ]
}

「谷歌表格数据体里的手数与时点残片」

这段 JSON 是某次 MT5 信号推送接口导出的表格数据残片,核心只塞了两列有效信息:手数 0.02,以及一笔标注为 2024-09-04 20:43 的时点记录。 其余从第三个数组元素开始全是 null,一直排到 parsedNumHeaders 显示表头数为 1,说明这张表原本设计了很多列,但实际只落了极少量字段。 在写 EA 接收第三方信号时,这种稀疏结构很常见——你不能假设每行都有值,遍历数组必须先用 if(v!=null) 过滤,否则容易把空手数当 0 手下单,在外汇和贵金属上引发非预期市价单,风险偏高。 直接把上面这段贴进 MT5 的 JSON 解析函数做反序列化,验证 null 跳过逻辑是否生效,比盲目信任信号源更稳妥。

MQL5 / C++
},
{
  "v": class="num">0.02,
  "f": "class="num">0.02"
},
{
  "v": "Date(class="num">2024,class="num">8,class="num">4,class="num">20,class="num">43,class="num">0)",
  "f": "class="num">2024-class="num">09-class="num">04 class="num">20:class="num">43"
}, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
{
  "v": null
}]
    }],
    "parsedNumHeaders": class="num">1
  }
});

用 EA 把 MT5 订单推到云表格

在 MT5 里落地一套跨账户交易日志,核心不是表格本身,而是让终端主动把成交数据往外推。Google Sheet 只承担云数据库角色,前面用 Apps Script 开了 HTTP 入口,这一步在早先的搭建里已经跑通,本次不重复。 新活落在 MetaTrader 侧:写一个自定义 EA,借助 WebRequest 走 HTTP POST,把 symbol、volume、order type 这些字段实时发到 Sheet。这样多账户、异地登录时,持仓与注释都能在同一张表里被监控和共享,外汇与贵金属品种波动大、滑点频发,跨账户核对前先确认券商允许外部 POST 请求。 EA 里关键就一行触发逻辑——订单成交事件里调 WebRequest 拼 JSON 出去,比手动导出 csv 更不容易漏单。

◍ 用 EA 把持仓与挂单推到在线表格

想在 MT5 里实时把账户里的未平仓头寸和挂单甩到 Google Sheet,得靠一个 EA 来干脏活。核心逻辑拆成两块就够了:一块负责把当前账户的仓位和待成交单捞出来,另一块负责把这批数据通过 HTTP 推走。 第一块建议写成独立函数,比如命名为 OrderList,它只做一件事——遍历账户,把每张单的子号、品种、方向、手数、开价、止损止盈捞成结构化数组。第二块命名为 UpdateDatabase,调用 WebRequest 把 OrderList 产出的内容 POST 到表格接口。 外汇与贵金属杠杆高,推送频率若设太密,WebRequest 超时可能漏单,实盘前先在策略测试器用模拟账户验证连通性。

「遍历终端里的活单与挂单」

要把 MT4/MT5 当前所有未平仓位和挂单抓出来,核心就是按总数倒序循环,避免删除或平仓导致索引错位。MQL5 用 PositionsTotal() 拿到持仓数,MQL4 用 OrdersTotal() 覆盖订单与持仓,两者都从 n-1 跑到 0。 MQL5 段先用 PositionGetTicket 取票据号,再用 PositionSelectByTicket 锁定,失败就 continue 跳过;随后用 PositionGetString / Integer / Double 把品种、类型、手数、开价、时间、SL、TP 读出来。MQL4 段则是 OrderSelect(conter, SELECT_BY_POS, MODE_TRADES) 选中后直接 OrderXxx() 系列取字段。 下面两段代码可直接贴进 EA 测:MQL5 版本在 0 号位起循环到 PositionsTotal()-1,MQL4 版本用 OrdersTotal()-1 同样倒序。跑起来后,若 UpdateDatabase 换成 Print 输出,能在专家日志看到每张活单的 ticket 与开仓价,外汇与贵金属杠杆高,活单暴露的 SL/TP 距离可能瞬间被扫,验证时先用模拟盘。

MQL5 / C++
class="type">void OrderList {
  for(class="type">int conter = PositionsTotal() - class="num">1; conter >= class="num">0; conter -= class="num">1)
  {
    const class="type">class="kw">ulong ticket = PositionGetTicket(conter);
    if(ticket == class="num">0 || !PositionSelectByTicket(ticket)) class="kw">continue;
    class="type">class="kw">string  symbol      =  PositionGetString(POSITION_SYMBOL);
    class="type">int     order_type  =  PositionGetInteger(POSITION_TYPE);
    class="type">class="kw">double  volume      =  PositionGetDouble(POSITION_VOLUME);
    class="type">class="kw">double  open_price  =  PositionGetDouble(POSITION_PRICE_OPEN);
    class="type">class="kw">datetime open_time  =  PositionGetInteger(POSITION_TIME);
    class="type">class="kw">double  stop_loss   =  PositionGetDouble(POSITION_SL);
    class="type">class="kw">double  take_profit =  PositionGetDouble(POSITION_TP);      
      class=class="str">"cmt">// Send trade details via WebRequest
      UpdateDatabase(symbol, order_type, volume, open_price, stop_loss, take_profit, ticket, open_time);
  }
} 
class="type">void OrderList {
  class=class="str">"cmt">// Loop through all open trades
  for(class="type">int conter = OrdersTotal() - class="num">1; conter >= class="num">0; conter -= class="num">1) {
    if(OrderSelect(conter, SELECT_BY_POS, MODE_TRADES)) {
      class=class="str">"cmt">// Collect trade data
      class="type">int     ticket      = OrderTicket();
      class="type">class="kw">string  symbol      = OrderSymbol();
      class="type">int     order_type  = OrderType();
      class="type">class="kw">double  volume      = OrderLots();
      class="type">class="kw">double  open_price  = OrderOpenPrice();
      class="type">class="kw">datetime open_time  = OrderOpenTime();
      class="type">class="kw">double  stop_loss   = OrderStopLoss();
      class="type">class="kw">double  take_profit = OrderTakeProfit();
      class=class="str">"cmt">// Send trade details via WebRequest
      UpdateDatabase(symbol, order_type, volume, open_price, stop_loss, take_profit, ticket, opentime);
    }
  }
}

把持仓快照推到 Google Sheet

拿到 OrderList 返回的逐单明细后,真正往外发数据的是另一个函数:它把交易品种、订单类型、手数、开仓价、止损、止盈、票号和开仓时间收齐,拼成 HTTP POST 体再送出。 URL 填的是 Google Apps Script 发布的公开表单接口,每人必须换成自己的脚本地址,否则数据会写进别人表里。 下面这段是核心发送逻辑。注意 datastring 用 & 拼接键值对,DoubleToString 按品种精度 digit 或固定 2 位格式化价格与手数;StringToCharArray 转 UTF-8 字节数组,WebRequest 走 POST,返回码 -1 代表请求层失败,需打印 GetLastError 排查。 实盘里每笔新单触发一次该函数,Sheet 会多一行记录;外汇与贵金属杠杆高,接口若卡顿可能漏单,建议先在模拟盘验证回写延迟。

MQL5 / C++
class="type">void SendTradeData(class="type">class="kw">string orderSymbol, class="type">int orderType, class="type">class="kw">double orderVolume, class="type">class="kw">double openPrice, class="type">class="kw">double stopLoss, class="type">class="kw">double takeProfit, class="type">class="kw">ulong orderTicket, class="type">class="kw">datetime openTime)
{ 
  class=class="str">"cmt">// Database url. Replace with your own url
  class="type">class="kw">string url = "https:class=class="str">"cmt">//script.google.com/macros/s/AfycbxWxYnDPsdvsvsdvdv236545OfgVnadvkvsASDCV6541scsSDHNfnqPeVkyH5A-r5eDhg/exec";
  class="type">int  digit = (class="type">int)SymbolInfoInteger(orderSymbol,SYMBOL_DIGITS);
  class="type">class="kw">string datastring = "?"
                    +"&ticket="+class="type">class="kw">string(orderTicket)
                    +"&order="+class="type">class="kw">string(orderType)
                    +"&symbol="+class="type">class="kw">string(orderSymbol)
  +"&openprice="+DoubleToString(openPrice,digit)
                    +"&sl="+DoubleToString(stopLoss,digit)
                    +"&tp="+DoubleToString(takeProfit,digit)
                    +"&close=class="num">0"
  +"&lot="+DoubleToString(orderVolume,class="num">2)
  +"&opentime="+(class="type">class="kw">string)openTime;  
  class="type">int  res;    class=class="str">"cmt">// To receive the operation execution result
  class="type">char  data[];    class=class="str">"cmt">// Data array to send POST requests  
  class="type">char  result[];
  class=class="str">"cmt">//--- Create the body of the POST request for authorization
  ArrayResize(data,StringToCharArray(datastring,data,class="num">0,WHOLE_ARRAY,CP_UTF8)-class="num">1);
  ResetLastError();
  res=WebRequest("POST", url, NULL, class="num">0, data, result, str);
  class="type">class="kw">string responsestring  = CharArrayToString(result, class="num">0, class="num">0, CP_UTF8);
  Print(responsestring);
  if(res == -class="num">1)
      Print("Error in WebRequest: ", GetLastError());
  else
      Print("Trade data sent: ", data);
}

◍ 用表格把每笔单子拆开算

MT5 每次成交,把开仓时间、品种、买卖方向、手数、平仓盈亏写进一张在线表,表里的图表就能实时反映你的全部历史仓位。 能算的东西很直接:买单数、卖单数、总手数、盈利单与亏损单各自的数量、平均盈利额、平均亏损额,以及任何你临时想到的切片维度。外汇与贵金属杠杆高,样本量太小的时候这些均值很容易失真,先看清楚分母再下结论。 我们做好的那张日记模板里,详细列字段都带了公式——点开链接看「列公式」那一栏,就能知道原始成交清单是怎么被汇总成分析结果的。复制一份改成自己的数据源,比从零搭表省事得多。

「记住这一条就够了」

把 MT5 里的成交明细自动推到 Google Sheets,本质就是 EA 里发一个 HTTP POST,靠 Google Apps Script 接住写表。原文给的 mq4 与 mq5 两个版本压缩包分别是 10.03 KB 和 9.38 KB,直接拖进对应终端就能跑,比手工记日志省事,也少抄错。 有读者在 5.00 build 5572 上编译片段报过 lvalue expected 的错,作者回应说文章里贴的是教学截断码,得用文末完整版才不会炸。外汇和贵金属杠杆高、滑点狠,这套日志只管记录不替你管控风险,别以为有了表就能乱开仓。 真要验证,就开 MT5 把文末 mq5 的 WebRequest 那行单独拎出来看: res=WebRequest("POST", url, NULL, 0, data, result, str); 这里 str 是出参容器,老版本终端或截断代码最容易在这翻车,更新到当前正式版再试一次最稳。

MQL5 / C++
res=WebRequest("POST", url, NULL, class="num">0, data, result, str);
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到订单与持仓的自动归集视图,把重复劳动交给小布,你专注决策。

常见问题

多为 Web 应用部署时权限未设为“任何人,甚至匿名”或脚本未重新部署新版本,需在 Apps Script 的“部署-为新版本”中确认访问设置。
小布盯盘已内置品种页的持仓与订单归集,可视为轻量日志;若需自定义字段写回表格,仍建议按本篇 EA 方案扩展。
将表格发布为 Web 应用并暴露 doGet,MT5 侧用 WebRequest 拉取 CSV 或 JSON,再解析比对本地 OrderSelect 结果。
优先看胜率、平均盈亏比与最大回撤,外汇贵金属波动剧烈,这些项能较快暴露策略脆弱点。
dev 链接仅限编辑器本人调试,正式 Web 应用须用 /exec 部署链接,否则外部 MT5 请求会被拒。