|
|
【工具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. |
//| BUY entry 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("AutoSLTP TP+", DoubleToString(TPOffset, 2), " SL-", DoubleToString(SLOffset, 2),
" magic=", FilterMagic, (AllSymbols ? " allSym" : " thisSym"),
" watched=", watched, " justSet=", applied);
}
//+------------------------------------------------------------------+复制代码或者直接下附件(懒得复制的)
拉到本帖末尾下文件(下载扣 5 活跃度,不想扣就复制上面的代码):MT4 下、MT5 下 。两个文件和上面代码是同一份。
有问题评论区聊。 |
|