Home > Blog > Invalid stops (10016/130) — il SL su MT5/MT4

MT5MQL5ErroriRisoluzione problemiEA

Invalid stops (10016/130) — il SL su MT5/MT4

Pubblicato: 2026-07-07Lettura: circa 8 min
This article reflects information as of its publish date. EA performance figures (PF, DD, annual return) change with live trading and re-validation — check the latest on the EA pages. See the latest EA results

Invalid stops (10016/130) risolto definitivamente

Quando si usa un EA e nella scheda Expert compare Invalid stops oppure OrderSend error 130, è facile pensare "il valore dello SL è sbagliato? Ma i calcoli dovrebbero essere corretti…". In realtà, la maggior parte di questi errori non deriva da un calcolo errato dello SL/TP, ma dalla violazione della "distanza minima" imposta dal broker. Anche se il valore in sé è corretto, l'ordine viene rifiutato se è troppo vicino al prezzo attuale, se ha la direzione invertita, oppure se rientra in una zona in cui la modifica è vietata.

Questo articolo è rivolto sia a chi utilizza EA su MT5/MT4 sia a chi sviluppa EA in MQL5, e riunisce in un'unica guida definitiva la natura di Invalid stops, le sue 6 cause, una diagnosi rapida in 30 secondi e le soluzioni permanenti a livello di codice. Per l'elenco completo dei codici di errore, consulta la guida generale ai codici di errore MQL5/MT5.

Questo articolo fa riferimento a MT5 (build serie 4xxx) a luglio 2026. I valori specifici dello stop level variano in base a broker e strumento.


Cos'è Invalid stops (differenza tra 10016 e 130)

Il valore che indica "stop non valido" esiste in due varianti, a seconda che si usi MT5 o MT4 (generazione della piattaforma). Capire su quale piattaforma e in quale fase compare l'errore accelera notevolmente la diagnosi.

① TRADE_RETCODE_INVALID_STOPS = 10016 (MT5 / codice di ritorno di OrderSend())

Il risultato di OrderSend() in MQL5 viene restituito in MqlTradeResult.retcode. Se lo SL/TP nella richiesta (o il rapporto con il prezzo di un ordine pendente) non rispetta le regole del server, la richiesta viene rifiutata con il codice 10016 (TRADE_RETCODE_INVALID_STOPS). Si tratta di una notifica di rifiuto proveniente dal server di trading.

Significato: gli stop (SL/TP) nella richiesta non sono validi (Invalid stops in the request)
Costante   : TRADE_RETCODE_INVALID_STOPS
Valore     : 10016
// Esempio tipico di output visibile nel log
2026.07.07 09:15:32.441 EA_NAME XAUUSD,M5: OrderSend error: retcode=10016 (invalid stops)

② ERR_INVALID_STOPS = 130 (MT4 / GetLastError())

Nella generazione MT4 (MQL4), quando OrderSend() / OrderModify() falliscono, GetLastError() restituisce il valore 130 (ERR_INVALID_STOPS). È questo che genera OrderSend error 130 nella scheda Expert. Se si utilizza anche un EA per MT4, è questo l'errore che comparirà.

Significato: stop non valido (invalid stops)
Costante   : ERR_INVALID_STOPS
Valore     : 130

In pratica:

PiattaformaFonteValoreQuando compare
MT5MqlTradeResult.retcode10016 (TRADE_RETCODE_INVALID_STOPS)OrderSend / PositionModify rifiutati dal server
MT5CTrade.ResultRetcode()10016Invio ordine/modifica rifiutati tramite CTrade
MT4GetLastError()130 (ERR_INVALID_STOPS)Dopo un fallimento di OrderSend / OrderModify

I numeri sono diversi, ma il significato e le cause sono praticamente identici, e anche la soluzione è comune. Da notare che il 10015 (TRADE_RETCODE_INVALID_PRICE) di MT5, spesso confuso con questo, indica che è il prezzo dell'ordine stesso ad essere non valido ed è un problema diverso. Il 10016 riguarda esclusivamente la "posizione" dello SL/TP (gli stop).


Diagnosi rapida in 30 secondi

Apri "Quotazioni di mercato → clic destro sul simbolo → Specifiche" in MT5 e controlla questi due valori.

Stop level (Stops level)     : distanza minima (in punti) a cui SL/TP devono trovarsi dal prezzo attuale
Freeze level (Freeze level)  : distanza (in punti) entro la quale è vietato modificare/annullare un ordine prossimo all'attivazione

Poi, confronta nel log i valori di SL/TP che si stava tentando di inviare con Bid/Ask attuali nel momento in cui è comparso l'errore.

  • SL o TP a una distanza dal prezzo attuale inferiore allo stop level → è quasi certamente questa la causa (causa ①).
  • BUY con SL sopra il Bid o TP sotto il Bid (il contrario per SELL) → direzione invertita (causa ②).
  • Fallisce solo la modifica di una posizione aperta → freeze level o impostazione SL/TP successiva all'apertura (cause ③ e ⑤).
  • Il valore di SL è un numero evidentemente non di prezzo, come "50" → confusione tra prezzo e punti (causa ④).

Per verificarlo da codice basta una riga.

Print("StopsLevel=", SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL),
      " FreezeLevel=", SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL));

Cause e soluzioni (6 pattern)

① SL/TP troppo vicini al prezzo attuale (sotto lo stop level)

Sintomo: frequente negli EA di scalping che posizionano lo SL molto stretto o con trailing dal margine ridotto. Anche manualmente, provando a piazzare lo SL "appena accanto" al prezzo, il pulsante di invio ordine risulta bloccato o l'ordine viene rifiutato.

Causa: il broker imposta per ogni strumento un valore SYMBOL_TRADE_STOPS_LEVEL (distanza minima dello stop, in punti), e il server rifiuta sistematicamente qualsiasi SL/TP o prezzo di ordine pendente a una distanza inferiore da quella dal prezzo attuale. Il prezzo di riferimento per la verifica è il Bid per SL/TP di una posizione BUY e l'Ask per una posizione SELL. Quando lo spread si allarga, la distanza tra Bid e Ask aumenta, per cui una distanza normalmente accettata può improvvisamente essere rifiutata durante le pubblicazioni di dati economici o nelle prime ore del mattino.

Soluzione:

  1. Verifica lo stop level nella finestra delle specifiche e amplia lo SL/TP o il margine di trailing dell'EA in modo che superi tale valore
  2. Applica un clamping lato EA prima dell'invio dell'ordine (vedi il codice più avanti)
  3. Se è indispensabile uno SL molto stretto, valuta un broker o un tipo di conto con uno stop level più basso

② Direzione di SL/TP invertita (scambio tra BUY/SELL)

Sintomo: l'errore Invalid stops compare sempre in una direzione specifica (solo acquisti o solo vendite). È il pattern più comune nei primi test di un EA sviluppato in proprio.

Causa: la regola è semplice: per un BUY, lo SL deve stare sotto il prezzo attuale (Bid) e il TP sopra; per un SELL, lo SL deve stare sopra il prezzo attuale (Ask) e il TP sotto. Errori tipici sono il copiare la formula di calcolo del BUY per il SELL dimenticando di invertire il segno, oppure scambiare price - sl con price + sl: il server restituisce immediatamente 10016/130.

Soluzione:

  1. Al momento dell'errore, stampa i valori reali con PrintFormat("type=%s sl=%.5f tp=%.5f bid=%.5f ask=%.5f", ...) e verifica visivamente la direzione
  2. Unifica il calcolo di SL/TP per BUY/SELL in una funzione comune, centralizzando in un unico punto la logica dei segni (eliminando le diramazioni copia-incolla)

③ Modifica di un ordine/posizione all'interno del freeze level

Sintomo: i nuovi ordini funzionano senza problemi, ma solo le modifiche/cancellazioni appena prima del TP, appena prima dello SL o appena prima dell'attivazione di un ordine pendente vengono rifiutate.

Causa: sugli strumenti in cui è impostato SYMBOL_TRADE_FREEZE_LEVEL, quando il prezzo attuale si avvicina fino a una certa distanza dal prezzo di attivazione (TP/SL o prezzo trigger di un ordine pendente), la modifica o l'annullamento di quell'ordine viene congelato. È una specifica del server pensata per evitare conflitti tra l'elaborazione dell'esecuzione e la richiesta di modifica, e si manifesta tipicamente quando il trailing dell'EA tenta un ulteriore aggiornamento proprio a ridosso del TP, venendo rifiutato.

Soluzione:

  1. Prima di ogni modifica, leggi SYMBOL_TRADE_FREEZE_LEVEL e, se la distanza dal prezzo di attivazione è pari o inferiore al freeze level, salta la modifica in quel ciclo
  2. Amplia l'intervallo e il margine di aggiornamento del trailing, riducendo le richieste di modifica inutili a ridosso dell'attivazione
  3. Il rifiuto in questo caso non è critico (essere vicini all'attivazione significa che l'esecuzione è comunque imminente), quindi è accettabile un design che si limiti a registrare l'errore nel log senza gestirlo ulteriormente

④ Confusione tra prezzo e punti (distanza)

Sintomo: nel campo SL viene inserito direttamente un "numero pensato come distanza", tipo 50 o 0.0050. Nel log, un valore come sl=50.00000 è evidentemente non un prezzo.

Causa: in MqlTradeRequest.sl / .tp va inserito il prezzo assoluto (non "50 punti sotto l'entrata"). Un EA che gestisce le distanze deve convertirle in entry ± distance * _Point prima di passarle. Al contrario, per abitudine derivata da alcune funzioni dei tempi di MT4, passare una distanza dove è richiesto un prezzo assoluto genera un valore che non è un prezzo valido, causando 10016/130.

Un altro errore classico è la confusione tra pips e punti. Sui broker a 5 cifre (ad es. USDJPY a 3 cifre, EURUSD a 5 cifre) 1 pip = 10 punti. Se "SL 50" viene interpretato come pips invece che come punti (o viceversa), la distanza risulta 10 volte diversa dal previsto e può scendere sotto lo stop level, causando il rifiuto.

Soluzione:

  1. Costruisci sempre SL/TP nella forma NormalizeDouble(price ± dist * _Point, _Digits)
  2. Indica esplicitamente in un commento l'unità di misura dei parametri di input (pips / points) e centralizza in un unico punto la conversione tramite _Point

⑤ Broker a esecuzione a mercato che non accetta SL/TP all'apertura della posizione

Sintomo: l'ordine viene eseguito senza SL/TP, ma un nuovo ordine con SL/TP genera Invalid stops. Si verifica in particolare su conti ECN/a esecuzione a mercato (Market Execution).

Causa: con l'esecuzione a mercato, il "prezzo al momento della richiesta" e il "prezzo effettivo di esecuzione" possono differire, per cui alcuni server non accettano lo SL/TP all'interno della richiesta di apertura, e richiedono di impostarli con una modifica della posizione dopo l'esecuzione (era una caratteristica classica dei conti ECN ai tempi di MT4, che generava spesso l'errore 130, e resta ancora oggi il comportamento di alcuni server anche su MT5).

Soluzione:

  1. Passa a uno schema in due fasi: prima invia l'ordine senza SL/TP → dopo la conferma dell'esecuzione, imposta SL/TP con PositionModify() (o trade.PositionModify() se usi CTrade)
  2. Con questo approccio esiste un momento in cui "l'ordine è andato a buon fine ma l'impostazione dello SL è fallita": inserisci sempre una protezione che ritenti l'impostazione dello SL fino a riuscirci, e chiuda immediatamente la posizione dopo un numero prestabilito di tentativi falliti (lasciare una posizione senza SL è l'esito peggiore)
  3. Puoi verificare la modalità di esecuzione con SymbolInfoInteger(_Symbol, SYMBOL_TRADE_EXEMODE)

⑥ Peculiarità specifiche dello strumento (oro e indici hanno stop level più ampi)

Sintomo: lo stesso EA funziona su EURUSD, ma appena viene applicato a XAUUSD (oro) o a un CFD su indice azionario, genera ripetutamente Invalid stops.

Causa: lo stop level è impostato individualmente per ogni strumento, ed è normale che oro, indici e valute esotiche abbiano valori più ampi rispetto alle valute principali. Riutilizzare senza modifiche uno SL o un margine di trailing stretto, calibrato sul forex maggiore, porta a non raggiungere la distanza minima richiesta dallo strumento, causando il rifiuto. Anche il numero di cifre decimali varia da strumento a strumento (l'oro ne ha tipicamente 2-3), per cui anche un codice che fissa _Digits in modo statico può rompersi.

Soluzione:

  1. Ogni volta che cambi strumento, verifica sempre stop level e numero di cifre nella finestra delle specifiche
  2. Imposta l'ampiezza di SL/TP non su punti fissi ma su un riferimento di volatilità come l'ATR, così da renderla più robusta al variare dello strumento
  3. Nel codice, recupera sempre in modo dinamico _Point / _Digits / SYMBOL_TRADE_STOPS_LEVEL (evita valori hardcoded)

Differenze tra broker (attenzione)

Stop level e freeze level variano completamente in base alla combinazione di broker e strumento. È normale che, con lo stesso EA e la stessa configurazione, un errore che non compare mai con il Broker A si presenti quotidianamente con il Broker B.

Un ulteriore punto di attenzione riguarda i broker che mostrano uno stop level pari a "0". Spesso 0 non significa "nessun limite", ma "valutazione dinamica": in condizioni normali qualsiasi SL molto vicino viene accettato, ma viene rifiutato proprio nei momenti in cui lo spread si allarga, ad esempio durante la pubblicazione di dati economici o nelle prime ore del mattino. La natura degli "Invalid stops che compaiono solo occasionalmente" è quasi sempre questa.

La verifica va sempre effettuata sul conto reale.

long stops  = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);   // in punti
long freeze = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL);  // in punti

I valori specifici dipendono dal tipo di conto e dalle specifiche dello strumento di ciascun broker, quindi non vengono riportati in questo articolo. L'unico valore corretto è quello ottenuto eseguendo il codice sopra sul proprio conto.


Codice MQL5 per prevenire questo errore (per gli sviluppatori di EA)

Il design corretto non è "correggere quando compare l'errore", ma eseguire un clamping di SL/TP alla distanza minima del broker prima dell'invio dell'ordine, in modo da non generare affatto il 10016.

Validare e limitare (clamp) SL/TP prima dell'invio dell'ordine

// Effettua il clamp di SL/TP a una distanza pari o superiore allo stop level prima dell'invio
// Valore restituito false = direzione invertita (errore di progettazione): non inviare l'ordine
bool ClampStops(ENUM_ORDER_TYPE type, double &sl, double &tp)
{
   double point   = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   int    digits  = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   long   stopsPt = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
   double spread  = SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                  - SymbolInfoDouble(_Symbol, SYMBOL_BID);
   // Margine pari a stop level + spread (protezione per broker con stops=0 a valutazione dinamica)
   double minDist = stopsPt * point + spread;

   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   if(type == ORDER_TYPE_BUY)
   {
      // Per un BUY, SL/TP vengono verificati rispetto al Bid
      if(sl > 0 && sl >= bid) return false;              // direzione invertita
      if(tp > 0 && tp <= bid) return false;
      if(sl > 0 && (bid - sl) < minDist) sl = bid - minDist;
      if(tp > 0 && (tp - bid) < minDist) tp = bid + minDist;
   }
   else if(type == ORDER_TYPE_SELL)
   {
      // Per un SELL, SL/TP vengono verificati rispetto all'Ask
      if(sl > 0 && sl <= ask) return false;              // direzione invertita
      if(tp > 0 && tp >= ask) return false;
      if(sl > 0 && (sl - ask) < minDist) sl = ask + minDist;
      if(tp > 0 && (ask - tp) < minDist) tp = ask - minDist;
   }
   sl = NormalizeDouble(sl, digits);
   tp = NormalizeDouble(tp, digits);
   return true;
}

I punti chiave sono tre.

  1. Recuperare sempre in modo dinamico SYMBOL_TRADE_STOPS_LEVEL e SYMBOL_POINT (rende il codice indipendente da strumento e broker)
  2. Aggiungere un margine pari allo spread (facilita il superamento del controllo anche su broker con stop level 0 a valutazione dinamica)
  3. Allineare sempre le cifre decimali alla fine con NormalizeDouble(prezzo, _Digits) (anche cifre decimali in eccesso possono causare il rifiuto)

Gestire singolarmente il retcode 10016

Registrare nel log, al momento del rifiuto, "cosa è stato inviato e quale fosse la distanza in quel momento" permette di individuare a colpo d'occhio quale delle cause ①-⑥ sia responsabile.

MqlTradeRequest req; MqlTradeResult res;
ZeroMemory(req); ZeroMemory(res);
// ... costruzione di req (sl/tp già passati per ClampStops) ...
if(!OrderSend(req, res) || res.retcode != TRADE_RETCODE_DONE)
{
   if(res.retcode == TRADE_RETCODE_INVALID_STOPS)   // 10016
      PrintFormat("Invalid stops: sl=%s tp=%s bid=%s stopsLevel=%d",
                  DoubleToString(req.sl, _Digits),
                  DoubleToString(req.tp, _Digits),
                  DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),
                  (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL));
   else
      PrintFormat("OrderSend failed: retcode=%d, lastError=%d", res.retcode, GetLastError());
}

Verificare il freeze level prima di modificare il trailing

// Prima di modificare una posizione, verifica che il prezzo di attivazione non sia nella zona di freeze
bool CanModify(double triggerPrice)
{
   long freeze = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL);
   if(freeze <= 0) return true;
   double dist = MathAbs(SymbolInfoDouble(_Symbol, SYMBOL_BID) - triggerPrice);
   return (dist > freeze * SymbolInfoDouble(_Symbol, SYMBOL_POINT));
}

Per un EA su MT4, gli stessi valori si ottengono con MarketInfo(Symbol(), MODE_STOPLEVEL) / MODE_FREEZELEVEL. La logica è del tutto identica.

Gli EA distribuiti da FXEA365 implementano come standard questa validazione di SL/TP prima dell'invio dell'ordine, il clamping dello stop level e il recupero dinamico indipendente dallo strumento, motivo per cui non si bloccano per Invalid stops anche cambiando broker o strumento.


Checklist di priorità

PrioritàVerificaSoluzione
🚨 Prima cosaLa distanza tra SL/TP e prezzo attuale è < dello stop level?Amplia SL/TP oppure implementa il clamping
🚨 Prima cosaLa direzione di SL/TP è invertita tra BUY/SELL?Stampa i valori reali nel log e verifica visivamente
⚠️ PoiFallisce solo la modifica → si è nel freeze level?Salta la modifica a ridosso dell'attivazione
⚠️ PoiIn sl/tp non viene inserita per errore una distanza (in punti)?Converti in prezzo assoluto price ± dist*_Point
✅ VerificaIl conto richiede l'impostazione di SL/TP dopo l'esecuzione a mercato?Schema in due fasi: invio ordine → PositionModify
🛠 SviluppoLe specifiche dello strumento vengono recuperate in modo dinamico?Implementa il ClampStops descritto sopra

Conclusione

  • Invalid stops corrisponde a 10016 (TRADE_RETCODE_INVALID_STOPS) su MT5 e a 130 (ERR_INVALID_STOPS) su MT4. I numeri sono diversi, ma il significato e la soluzione sono comuni.
  • Nella maggior parte dei casi la causa non è un errore di calcolo, ma una delle seguenti 6: distanza inferiore allo stop level, direzione invertita, freeze level, confusione tra prezzo e punti, impostazione di SL/TP successiva all'apertura, peculiarità specifiche dello strumento.
  • Gli sviluppatori possono risolvere il problema in modo permanente con "lettura di SYMBOL_TRADE_STOPS_LEVEL e clamping + NormalizeDouble prima dell'invio dell'ordine". Anche i broker con stop level a 0 vengono gestiti aggiungendo un margine pari allo spread.

Per i codici di errore in generale, consulta la guida completa ai codici di errore MQL5/MT5. Per un altro errore classico che blocca l'invio degli ordini, la "mancanza di fondi", trovi l'approfondimento nell'articolo dedicato a ERR_NO_MONEY (134/10019).


FAQ

D: Il calcolo di SL/TP dovrebbe essere corretto, eppure compare Invalid stops. Perché?

È molto probabile che il rifiuto non dipenda dalla correttezza del valore, ma dalla distanza dal prezzo attuale. Uno SL/TP inferiore alla distanza minima (stop level) imposta dal broker viene sistematicamente rifiutato, anche se il valore è preciso. Verifica la distanza minima nella finestra delle specifiche oppure con SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL).

D: Che differenza c'è tra 10016 e 130?

10016 (TRADE_RETCODE_INVALID_STOPS) è il codice di risultato restituito da OrderSend() / PositionModify() su MT5, mentre 130 (ERR_INVALID_STOPS) è il valore restituito da GetLastError() su MT4. Cambia solo la piattaforma: il significato (posizione non valida di SL/TP) e la soluzione sono identici.

D: Normalmente non compare, ma solo durante la pubblicazione di dati economici ottengo Invalid stops.

La causa è l'allargamento dello spread. Poiché SL/TP di un BUY vengono verificati rispetto al Bid e quelli di un SELL rispetto all'Ask, proprio nel momento in cui lo spread si allarga la distanza può risultare insufficiente. Anche sui broker con stop level pari a 0 può verificarsi un rifiuto dinamico solo nei momenti di variazione improvvisa. Aggiungi un margine pari allo spread alla distanza di SL/TP (vedi il codice nell'articolo).

D: Senza SL/TP l'ordine passa, ma con SL/TP viene rifiutato.

Su un conto a esecuzione a mercato (Market Execution), è possibile che il server non accetti SL/TP all'interno della richiesta di apertura di un nuovo ordine. Passa a uno schema in due fasi: invia l'ordine senza SL/TP e impostali con PositionModify() dopo l'esecuzione. Inserisci comunque sempre un meccanismo di retry in caso di fallimento dell'impostazione dello SL e una protezione di chiusura immediata in caso di fallimenti ripetuti.

D: Un EA che funziona su EURUSD genera ripetutamente Invalid stops sull'oro.

È normale che oro e indici azionari abbiano uno stop level più ampio rispetto alle valute principali, e anche il numero di cifre decimali è diverso. Amplia lo SL o il margine di trailing in base alle specifiche dello strumento, oppure passa a un margine legato alla volatilità basato sull'ATR.

📧 Avvisi prima degli aumenti di prezzo + corso email gratuito di 5 giorni

Tutti gli EA sono a prezzo di lancio e salgono a scatti con le vendite. Ricevi un avviso prima di ogni aumento, più un'email al giorno su trading algoritmico, lettura dei backtest e scelta del broker.

* Privacy rigorosamente protetta. Potete cancellarvi in qualsiasi momento.

Commenti e domande