paullhou 发表于 2026-8-24 20:44:52

【AutoSLTP工具EA·带源码】手机下单自动挂止盈止损 AutoSLTP(MT4/MT5通用)

【工具EA·带源码】手机下单自动挂止盈止损 AutoSLTP(MT4/MT5 通用)
手机下单最常见的坑:急着进场,忘了或来不及挂止盈止损,结果行情一反手,一张没挂保护的单能吃掉你一大块利润。这个小 EA 就干一件事:你手机/手动下的单,它自动按你设好的距离,替你把止盈止损补上。
一套源码 MT4、MT5 都能用,纯 ASCII,附件直接下。
⚠️ 先说清楚:这是 EA,得有一台一直开机的电脑或 VPS 挂着它才管用,电脑关了它就停。 只用手机、或电脑会关机的,先往下看"必读"那节再决定要不要用。它做什么
挂到图表上后,它盯着这个账户的持仓——包括你用手机 App 下的单(只要是同一个账户就能看到):

[*]你 4569 进多,设了 TP 偏移 10、SL 偏移 10 → 它自动给这单挂上 止盈 4579、止损 4559;
[*]偏移你自己设(按价格距离,黄金 10 = $10 = 1000 点);哪个设成 0 就是不挂那一项(只想要止损、不要止盈,把 TP 设 0 即可);
[*]按开仓价算,不是现价——你 4569 进的,TP 就是 4569±偏移,和当前价无关。
⭐三个让它"不添乱"的设计

[*]只在"你没设"时才补,已经设好的绝不动。 你手动挂了止损没挂止盈,它只补止盈,你的止损原样保留;两个都挂好了,它完全不碰这单。(想让它强制覆盖,把 Overwrite 改 true,默认不覆盖。)
[*]只管你手动/手机下的单(FilterMagic=0),不碰其它 EA 开的单,和你别的策略互不干扰。(注:手动/手机单的 magic 通常是 0;如果你某个 EA 也用 magic=0,那它的单也会被一起管,这种情况把 FilterMagic 改成别的值避开。)
[*]默认管全账户所有品种(AllSymbols=true),你手机下黄金、下外汇都一起照顾;只想管一个品种就设 false。
参数

参数默认说明
TPOffset10止盈距开仓价(价格距离);0 = 不挂止盈
SLOffset10止损距开仓价(价格距离);0 = 不挂止损
FilterMagic0只管这个 magic 的单(0 = 手动/手机单);-1 = 全部
AllSymbolstruetrue = 管全账户所有品种;false = 只管当前图表品种
Overwritefalsefalse = 只在没设时才补;true = 强制按开仓价覆盖
RefreshMs500多久扫一次(毫秒)
⚠️⚠️ 必读:它必须挂在"一直开机"的电脑或服务器上
这是 EA,不是手机 App 里的功能。它要靠一个开着 MT4/MT5 的终端在后台运行才生效:

[*]推荐用 VPS(服务器):24 小时在线,你人不在、手机在外面下单,它照样第一时间帮你补止盈止损;
[*]没有 VPS、只用自己电脑的: 只有「电脑开着 + MT4/MT5 开着 + 允许自动交易」这三条同时满足时它才工作。你一关机、一关 MT4,它立刻停 —— 之后手机下的单不会再被自动挂止损。别以为关了电脑它还在替你盯盘。
一句话:要它真正"永远在帮你兜底",就得有个不关机的地方(VPS)挂着。 只用个人电脑的,记得"电脑关=保护停"。
装法

[*]把 AutoSLTP.mq4(MT4)或 AutoSLTP.mq5(MT5)放进 MQL4/Experts(或 MQL5/Experts);
[*]MetaEditor 里 F7 编译(0 错误);
[*]拖到任意一个图表上,勾允许自动交易;顶部"自动交易"按钮要是绿的;
[*]之后你手机随便下单,不挂止盈止损,它半秒内自动补上。
一点说明

[*]偏移单位是价格(黄金 10 = $10)。若你的品种小数位不同(比如外汇),按该品种的价格来填距离;
[*]如果偏移设得太小、离现价比经纪商最小止损距离还近,经纪商会拒绝,EA 会在日志里提示"level too close",这时把偏移设大一点即可;
[*]MT5 净持仓(netting)账户:同品种加仓会合并成一张单、开仓价变成均价,而本 EA 只在"没设过"时挂一次、不会按新均价重挂——经常加仓的朋友这点自己留意(对冲/MT4 每单独立则无此问题);
[*]源码开放,逻辑就几十行,能读能改,欢迎自己按需魔改。
完整源码(复制即用,免扣活跃度)
不想下载扣活跃度的,直接复制下面这份,存成 .mq4(给 MT4)或 .mq5(给 MT5),放进 Experts 编译即可——两个平台同一份代码,靠 #ifdef __MQL5__ 自动分平台:
//+------------------------------------------------------------------+
//| AutoSLTP (MT4 + MT5, one source via #ifdef)                      |
//| Attach to a chart. When a position appears on this account       |
//| (incl. orders you place by hand from the phone), it auto-sets a|
//| fixed TP and/or SL at a chosen price distance from the ENTRY.    |
//|   BUYentry E: TP = E + TPOffset , SL = E - SLOffset            |
//|   SELL entry E: TP = E - TPOffset , SL = E + SLOffset            |
//| Offset unit = PRICE (for gold 10 = $10 = 1000 points). 0 = skip. |
//| MT5: compile as .mq5. MT4: save the SAME content as .mq4.      |
//+------------------------------------------------------------------+
#property strict
#property version   "1.00"

#ifdef __MQL5__
#include <Trade/Trade.mqh>
CTrade trade;
#endif

input double TPOffset   = 10.0;   // TP distance from entry, in price (0 = do NOT set TP)
input double SLOffset   = 10.0;   // SL distance from entry, in price (0 = do NOT set SL)
input int    FilterMagic= 0;      // only touch this magic (0 = manual/phone orders). -1 = all
input bool   AllSymbols = true;   // true = manage every symbol; false = only this chart symbol
input bool   Overwrite= false;// false = only set when SL/TP empty; true = always enforce
input int    RefreshMs= 500;    // scan interval (ms)

int OnInit()
{
#ifdef __MQL5__
   trade.SetDeviationInPoints(30);
#endif
   EventSetMillisecondTimer(RefreshMs > 100 ? RefreshMs : 100);
   Scan();
   return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason) { EventKillTimer(); Comment(""); }
void OnTimer() { Scan(); }

double Rnd(string sym, double px)
{
#ifdef __MQL5__
   int dg = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS);
#else
   int dg = (int)MarketInfo(sym, MODE_DIGITS);
#endif
   return NormalizeDouble(px, dg);
}
double MinStopDist(string sym)
{
#ifdef __MQL5__
   long lvl = SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL);
   double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
   return (double)lvl * pt;
#else
   return MarketInfo(sym, MODE_STOPLEVEL) * MarketInfo(sym, MODE_POINT);
#endif
}
double SymPx(string sym, bool wantBid)
{
#ifdef __MQL5__
   return SymbolInfoDouble(sym, wantBid ? SYMBOL_BID : SYMBOL_ASK);
#else
   return MarketInfo(sym, wantBid ? MODE_BID : MODE_ASK);
#endif
}
bool Plan(string sym, int dir, double entry, double curSL, double curTP,
          double &newSL, double &newTP)
{
   newSL = curSL; newTP = curTP;
   if(TPOffset > 0.0 && (Overwrite || curTP == 0.0))
      newTP = Rnd(sym, (dir == 0) ? entry + TPOffset : entry - TPOffset);
   if(SLOffset > 0.0 && (Overwrite || curSL == 0.0))
      newSL = Rnd(sym, (dir == 0) ? entry - SLOffset : entry + SLOffset);
   return (MathAbs(newSL - curSL) > _Point || MathAbs(newTP - curTP) > _Point);
}
bool FarEnough(string sym, int dir, double lvlPrice)
{
   if(lvlPrice == 0.0) return true;
   double md = MinStopDist(sym);
   double ref = (dir == 0) ? SymPx(sym, true) : SymPx(sym, false);
   return MathAbs(ref - lvlPrice) >= md;
}
void Scan()
{
   int applied = 0, watched = 0;
#ifdef __MQL5__
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong tk = PositionGetTicket(i);
      if(tk == 0 || !PositionSelectByTicket(tk)) continue;
      string sym = PositionGetString(POSITION_SYMBOL);
      if(!AllSymbols && sym != _Symbol) continue;
      if(FilterMagic >= 0 && PositionGetInteger(POSITION_MAGIC) != FilterMagic) continue;
      long ty = PositionGetInteger(POSITION_TYPE);
      if(ty != POSITION_TYPE_BUY && ty != POSITION_TYPE_SELL) continue;
      int dir = (ty == POSITION_TYPE_BUY) ? 0 : 1;
      double entry = PositionGetDouble(POSITION_PRICE_OPEN);
      double curSL = PositionGetDouble(POSITION_SL), curTP = PositionGetDouble(POSITION_TP);
      watched++;
      double nSL, nTP;
      if(!Plan(sym, dir, entry, curSL, curTP, nSL, nTP)) continue;
      if(!FarEnough(sym, dir, nSL) || !FarEnough(sym, dir, nTP))
      { Print("AutoSLTP: level too close for #", tk, " - skipped"); continue; }
      if(trade.PositionModify(tk, nSL, nTP)) applied++;
      else Print("AutoSLTP: modify fail #", tk, " ret=", trade.ResultRetcode());
   }
#else
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      string sym = OrderSymbol();
      if(!AllSymbols && sym != _Symbol) continue;
      if(FilterMagic >= 0 && OrderMagicNumber() != FilterMagic) continue;
      int type = OrderType();
      if(type != OP_BUY && type != OP_SELL) continue;
      int dir = (type == OP_BUY) ? 0 : 1;
      double entry = OrderOpenPrice();
      double curSL = OrderStopLoss(), curTP = OrderTakeProfit();
      watched++;
      double nSL, nTP;
      if(!Plan(sym, dir, entry, curSL, curTP, nSL, nTP)) continue;
      if(!FarEnough(sym, dir, nSL) || !FarEnough(sym, dir, nTP))
      { Print("AutoSLTP: level too close for #", OrderTicket(), " - skipped"); continue; }
      if(OrderModify(OrderTicket(), OrderOpenPrice(), nSL, nTP, 0, clrNONE)) applied++;
      else Print("AutoSLTP: modify fail #", OrderTicket(), " err=", GetLastError());
   }
#endif
   Comment("AutoSLTPTP+", DoubleToString(TPOffset, 2), " SL-", DoubleToString(SLOffset, 2),
         "magic=", FilterMagic, (AllSymbols ? " allSym" : " thisSym"),
         "watched=", watched, " justSet=", applied);
}
//+------------------------------------------------------------------+或者直接下附件(懒得复制的)
拉到本帖末尾下文件(下载扣 5 活跃度,不想扣就复制上面的代码):MT4 下、MT5 下 。两个文件和上面代码是同一份。
有问题评论区聊。

yoke 发表于 2026-8-28 18:23:20

这个检测很灵敏

ziyan 发表于 2026-9-1 21:40:44

带个追踪止损多好

小白龙游 发表于 2026-9-9 17:22:00

这个好,感谢分享

paullhou 发表于 2026-9-11 13:39:52

ziyan 发表于 2026-9-1 21:40
带个追踪止损多好

非常好的idea

1578783018 发表于 2026-10-6 21:49:21

感谢分享

1578783018 发表于 2026-10-8 20:11:45

挺简单的一个止盈止损工具

paullhou 发表于 2026-10-9 09:07:14

1578783018 发表于 2026-10-8 20:11
挺简单的一个止盈止损工具

是的,但是很有用啊
页: [1]
查看完整版本: 【AutoSLTP工具EA·带源码】手机下单自动挂止盈止损 AutoSLTP(MT4/MT5通用)