MT5 금 EA, 브로커를 옮기면 주문이 안 나가는 이유

2026-11-11 알고랩

결론부터 말하면, MT5에서는 거래 조건을 브로커 서버가 정합니다. 금 심볼 이름, 가격 자릿수, 주문 체결 방식, 손절을 걸 수 있는 최소 거리, 계좌 방식, 서버 시간이 브로커마다 다릅니다. EA가 이 값들을 숫자로 박아 두고 있으면 브로커를 옮긴 날부터 주문이 거절되고, MT5 저널에는 10030이나 10016 같은 코드가 찍힙니다.

저희도 당한 적이 있습니다. 2026년 9월에 나스닥 CFD용 MT5 EA를 납품하면서 데모 계좌에서 먼저 돌렸는데, 첫날 신호가 두 번 떴고 두 번 다 진입을 못 했습니다. 스프레드가 100포인트를 넘으면 진입을 미루게 해 뒀는데 그 계좌는 평소 스프레드가 180포인트였습니다. 외환 종목 감각으로 숫자를 잡은 게 잘못이었습니다. 한도를 고치고 사흘을 더 돌린 뒤에 넘겼습니다. 그때 기록은 MT5 NAS100 EA 제작 사례에 있습니다.

그 뒤로는 EA를 만들거나 옮길 때 아래 순서로 확인합니다. 금(XAUUSD) 기준으로 적었습니다.

먼저 종목 사양을 찍어 봅니다

EA가 켜질 때 그 브로커의 조건을 저널에 한 줄 남기게 합니다. 나중에 주문이 안 나간 이유를 찾을 때 이 한 줄부터 봅니다.

SpecCheck.mq5

void PrintSpec(const string s)
{
   PrintFormat("%s digits=%d point=%.3f contract=%.0f volmin=%.2f step=%.2f stops=%d freeze=%d spread=%d",
      s,
      (int)SymbolInfoInteger(s, SYMBOL_DIGITS),
      SymbolInfoDouble(s, SYMBOL_POINT),
      SymbolInfoDouble(s, SYMBOL_TRADE_CONTRACT_SIZE),
      SymbolInfoDouble(s, SYMBOL_VOLUME_MIN),
      SymbolInfoDouble(s, SYMBOL_VOLUME_STEP),
      (int)SymbolInfoInteger(s, SYMBOL_TRADE_STOPS_LEVEL),
      (int)SymbolInfoInteger(s, SYMBOL_TRADE_FREEZE_LEVEL),
      (int)SymbolInfoInteger(s, SYMBOL_SPREAD));
}

int OnInit()
{
   PrintSpec(_Symbol);
   return(INIT_SUCCEEDED);
}

계좌 두 개에서 돌리면 이런 식으로 다르게 나옵니다. 아래 숫자는 형태를 보여 주려고 지어낸 예시입니다.

XAUUSD  digits=2 point=0.010 contract=100 volmin=0.01 step=0.01 stops=0  freeze=0 spread=25
XAUUSDm digits=3 point=0.001 contract=100 volmin=0.01 step=0.01 stops=50 freeze=0 spread=180

심볼 이름부터 다릅니다. XAUUSD가 흔하지만 GOLD로 올려 둔 브로커도 있고, 계좌 유형에 따라 XAUUSDm이나 XAUUSD.a처럼 뒤에 글자가 붙기도 합니다. 코드에 "XAUUSD"라고 적어 두면 이름이 다른 계좌에서는 시세도 못 받습니다. EA를 올린 차트의 심볼인 _Symbol을 쓰면 이 문제는 없습니다.

더 조심할 건 자릿수입니다. 금 가격을 소수 둘째 자리까지 주는 브로커와 셋째 자리까지 주는 브로커가 있습니다. MT5는 손절 폭을 포인트로 다루는데 포인트 하나가 한쪽은 0.01달러, 다른 쪽은 0.001달러입니다. 손절 300포인트가 3달러도 되고 0.3달러도 됩니다. 그래서 손절 폭은 달러로 입력받고 EA가 포인트로 바꾸게 만듭니다. 1랏이 몇 온스인지도 브로커마다 다를 수 있어서 가정하지 않고 읽습니다.

주문이 전부 거절될 때

브로커를 옮긴 EA에서 제일 자주 보는 건 이 줄입니다.

failed market buy 0.10 XAUUSD sl: 4085.20 tp: 4112.40 [Unsupported filling mode]

거래 서버 반환 코드로는 10030, 상수 이름은 TRADE_RETCODE_INVALID_FILL입니다. 주문 물량이 한 번에 다 안 채워질 때 어떻게 할지를 주문마다 적어 보내야 하는데, 전량 아니면 취소(ORDER_FILLING_FOK), 되는 만큼만(ORDER_FILLING_IOC), 남은 물량 유지(ORDER_FILLING_RETURN) 중에 무엇을 받는지가 브로커와 심볼마다 다릅니다. EA가 하나로 고정해서 보내면 안 받는 브로커에서는 모든 주문이 거절됩니다.

허용되는 값을 읽어서 고르면 됩니다.

ENUM_ORDER_TYPE_FILLING PickFilling(const string s)
{
   long mode = SymbolInfoInteger(s, SYMBOL_FILLING_MODE);
   if((mode & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) return ORDER_FILLING_FOK;
   if((mode & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC) return ORDER_FILLING_IOC;
   return ORDER_FILLING_RETURN;
}

MqlTradeRequest req = {};
req.action       = TRADE_ACTION_DEAL;
req.symbol       = _Symbol;
req.type_filling = PickFilling(_Symbol);

다만 MQL5 포럼에는 심볼 속성이 알려 주는 값과 서버가 실제로 받는 값이 달랐다는 글도 있습니다. 그래서 OrderSend가 돌려주는 코드를 매번 로그에 남기고, 데모 계좌에서 실제로 주문을 내 봅니다. ChatGPT로 짠 EA가 컴파일은 되는데 주문을 못 낸다면 여기부터 보세요. 증권사 API에서 비슷하게 막히는 곳은 ChatGPT 자동매매 코드가 막히는 6곳에 적어 뒀습니다.

손절이 안 걸릴 때

브로커는 현재가에 너무 붙은 손절과 익절을 받지 않습니다. 그 최소 거리가 SYMBOL_TRADE_STOPS_LEVEL이고 단위는 포인트입니다. 이보다 가깝게 걸면 10016(TRADE_RETCODE_INVALID_STOPS)이 돌아오고 저널에는 [Invalid stops]라고 찍힙니다. 트레일링 스톱을 촘촘하게 옮기는 EA는 SYMBOL_TRADE_FREEZE_LEVEL에도 걸립니다. 가격이 주문가에 이만큼 다가오면 수정과 취소를 막는 거리입니다.

double MinStopDistance(const string s)
{
   int stops  = (int)SymbolInfoInteger(s, SYMBOL_TRADE_STOPS_LEVEL);
   int spread = (int)SymbolInfoInteger(s, SYMBOL_SPREAD);
   return (stops + spread) * SymbolInfoDouble(s, SYMBOL_POINT);
}

스톱 레벨이 0으로 나오는 계좌도 있습니다. 제한이 없거나 그때그때 바뀐다는 뜻이라 스프레드만큼은 띄워 둡니다. 금은 미국 고용지표나 FOMC 직후, 하루 거래가 끝났다가 다시 열릴 때 스프레드가 크게 벌어집니다. 스프레드가 한도보다 넓으면 진입을 미루게 하는데, 맨 위에 적은 실수가 바로 이 한도였습니다. 진입을 막았으면 왜 막았는지를 로그에 남겨야 합니다. 그때도 로그에 사유가 찍혀 있어서 5분 만에 찾았습니다. 손절 폭을 변동성에 맞추는 방법은 ATR 손절과 고정 폭 손절을 비교한 글에 있습니다.

롱과 숏을 같이 드는 전략이면

MT5 계좌는 헤징과 네팅 두 가지입니다. 헤징 계좌는 같은 심볼에 매수와 매도를 따로 들 수 있습니다. 네팅 계좌는 심볼당 포지션이 하나라서 매수 1랏을 든 채로 매도 1랏을 내면 포지션이 없어집니다. 금에서 많이 쓰는 양방향 그리드나 헷지 마틴게일은 네팅 계좌에서는 돌지 않습니다. 계좌를 만들 때 고르는 항목이라 나중에 바꾸기도 어렵습니다.

bool IsHedging()
{
   return (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)
          == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING;
}

양방향 전략 EA는 시작할 때 이걸 확인하고 네팅 계좌면 켜지지 않게 합니다. 조용히 돌다가 포지션이 사라지는 것보다 낫습니다.

헤징 계좌는 증거금 계산도 다릅니다. 양방향을 같이 들면 큰 쪽만 증거금으로 잡는 브로커가 많고, 마진 레벨 몇 퍼센트에서 강제청산이 걸리는지도 브로커마다 다릅니다. 물량이 계단식으로 불어나는 전략은 이 숫자가 계좌가 버티는 한계입니다. MT4로 골드 양방향 EA를 만들 때는 같은 규칙의 파이썬 백테스터를 먼저 만들어서 몇 회차에 청산되는지부터 봤습니다. 그 사례에 화면이 있습니다. 레버리지 상품에 넣는 안전장치는 선물과 레버리지 자동매매 글에 따로 정리했습니다.

시간은 서버 시간입니다

MT5 차트의 시각은 브로커 서버 시간입니다. GMT+2로 두다가 서머타임에 GMT+3으로 바꾸는 곳이 많고 GMT+0인 곳도 있습니다. "한국 시간 오전 9시부터 매매"를 서버 시각 숫자로 적어 두면 브로커를 바꾸거나 서머타임이 바뀌는 날 한 시간씩 밀립니다. 실거래에서는 서버 시각과 GMT의 차이를 돌면서 잽니다.

int ServerOffsetSec()
{
   return (int)(TimeTradeServer() - TimeGMT());
}

datetime KstNow() { return TimeGMT() + 9 * 3600; }

이 코드는 전략 테스터에서는 통하지 않습니다. 테스터 안에서는 TimeGMT()가 서버 시각과 같은 값으로 나와서 시차가 0으로 계산됩니다. 백테스트에서 세션 시각을 맞추려면 그 브로커의 겨울 시차와 서머타임 규칙을 입력으로 받아 따로 계산해야 합니다.

금은 하루에 한 번 쉬는 구간이 있고 주말에는 닫힙니다. 쉬는 시각은 브로커의 종목 사양에 요일별로 적혀 있고 코드에서는 SymbolInfoSessionTrade로 읽습니다. 그 시간에 주문을 내면 10018(TRADE_RETCODE_MARKET_CLOSED)이 돌아옵니다. EA를 올린 PC나 VPS의 시계가 틀어지면 로그 시각이 어긋나서 원인을 찾기 어려워지니 윈도우 시간 동기화도 한 번 확인해 두세요. 24시간 돌릴 환경은 VPS 운영 글에 있습니다.

테스터 결과는 어디까지 믿나

전략 테스터(Strategy Tester)는 브로커 서버에서 받은 과거 데이터를 씁니다. 브로커마다 틱과 스프레드 기록이 달라서 같은 EA를 같은 기간에 돌려도 결과가 다릅니다. 모델링을 '실제 틱 기반 모든 틱'으로 두면 실거래에 가장 가깝고, '1분 OHLC'나 '시가만'은 빠른 대신 봉 안에서 손절과 익절 중 어느 쪽이 먼저 닿았는지를 추정합니다. 손절 폭이 좁은 금 단타 EA는 이 차이가 큽니다. 백테스트와 실거래가 벌어지는 이유는 이 글에 더 있습니다.

저희는 테스터로는 규칙이 맞게 구현됐는지만 봅니다. 넘기기 전에는 고객이 실제로 쓸 브로커의 데모 계좌에서 며칠 돌립니다. 나스닥 EA는 사흘 동안 진입 16건, 청산 15건을 로그로 대조했고 저널 오류는 0건이었습니다. 데모에서는 수익을 보지 않습니다. 아래 코드가 저널에 한 번이라도 찍히는지를 봅니다.

코드상수뜻
10004TRADE_RETCODE_REQUOTE가격이 바뀌어 다시 호가
10016TRADE_RETCODE_INVALID_STOPS손절, 익절 거리 위반
10018TRADE_RETCODE_MARKET_CLOSED거래 시간이 아님
10019TRADE_RETCODE_NO_MONEY증거금 부족
10027TRADE_RETCODE_CLIENT_DISABLES_ATMT5에서 자동매매 버튼이 꺼져 있음
10030TRADE_RETCODE_INVALID_FILL체결 방식을 받지 않음

제작을 맡기실 때

견적 전에 어느 브로커, 어떤 계좌에서 돌리실지를 먼저 여쭙니다. 같은 브로커라도 계좌 유형에 따라 심볼 이름과 스프레드가 달라서입니다. 아직 못 정하셨으면 그렇다고 말씀해 주세요. 조건을 읽어서 쓰는 구조로 만들어 두고 브로커가 정해지면 그때 데모에서 확인합니다.

MT5 종목 목록에서 금을 오른쪽 클릭하고 '사양'을 열어 그 창을 캡처해 보내 주시면 자릿수, 계약 크기, 최소 랏, 스톱 레벨, 거래 시간을 한 번에 볼 수 있습니다. 계좌가 헤징인지 네팅인지도 같이 알려 주세요. 여기에 매매 규칙과 돌릴 곳(내 PC인지 VPS인지)만 더하면 견적을 낼 수 있습니다. 지금까지 만든 MT5 EA는 2~3주쯤 걸렸습니다. 제작 방식은 MT5 EA 제작 안내에 있습니다.

자주 받는 질문

다른 브로커에서 쓰던 MT5 금 EA를 그대로 옮겨도 되나요?

그대로 돌아가는 경우도 있지만 실계좌에 바로 올리지는 마세요. 심볼 이름, 가격 자릿수, 체결 방식, 손절 최소 거리, 계좌 방식, 서버 시간이 브로커마다 다릅니다. 새 브로커의 데모 계좌에서 며칠 돌려 보고 저널에 10030이나 10016 같은 거절이 없는지 먼저 확인하세요.

MT5에서 Unsupported filling mode(10030)는 왜 나오나요?

EA가 주문에 넣은 체결 방식(FOK, IOC, RETURN 중 하나)을 그 브로커의 그 심볼이 받지 않을 때 나옵니다. 체결 방식을 코드에 고정하지 말고 SymbolInfoInteger로 SYMBOL_FILLING_MODE를 읽어서 고르면 대부분 해결됩니다.

MT5 금 EA 제작을 맡길 때 무엇을 알려 줘야 하나요?

브로커와 계좌 유형, MT5에서 금 종목의 사양 창 캡처, 헤징 계좌인지 네팅 계좌인지를 알려 주시면 됩니다. 사양 창에 자릿수, 계약 크기, 최소 랏, 스톱 레벨, 거래 시간이 다 나옵니다.

EA를 만들거나 고치실 일이 있으면

브로커와 종목, 원하는 매매 방식을 채팅으로 알려 주세요. 만들던 EA가 있으면 그대로 가져오셔도 됩니다.

채팅으로 문의하기

2026년 11월 11일 기준 MQL5 공식 문서를 보고 썼습니다. 브로커별 실제 값은 수시로 바뀌니 각 브로커의 종목 사양에서 확인하세요. 특정 브로커를 추천하지 않습니다. FX, CFD 같은 고레버리지 마진 거래는 원금 전액을 넘는 손실이 날 수 있습니다. 알고랩은 프로그램 제작 도구를 제공하며 투자자문업과 투자일임업을 영위하지 않습니다.