English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5로 하는 트레이드 오퍼레이션 - 어렵지 않아요!

MQL5로 하는 트레이드 오퍼레이션 - 어렵지 않아요!

MetaTrader 5 | 5 7월 2021, 11:40
169 0
MetaQuotes
MetaQuotes

대부분의 투자자들이 수익 창출을 목적으로 하지만 투자 과정 자체를 즐기는 투자자들도 있습니다. 하지만 반드시 수동 매매여야만 그 과정이 즐거울 수 있는 건 아니예요. 자동 거래 시스템 개발 또한 꽤 흥미진진하답니다. 트레이딩 로봇 개발은 좋은 추리 소설을 읽는 것 만큼이나 재밌을 수 있어요.

알고리즘을 설계할 때 꽤 많은 테크니컬 이슈를 고려해야 하는데요. 그 중에서도 가장 중요한 몇몇은 다음과 같습니다:

  1. 무엇을 거래할 것인가?
  2. 언제 거래할 것인가?
  3. 어떻게 거래할 것인가?

자신에게 가장 적합한 심볼을 고르려면 첫 번째 질문에 대한 답을 생각해 보아야 합니다. 이 경우 자동 매매 시스템 구축 가능 여부를 포함한 여러 요인에 선택이 좌우됩니다. 두 번째 질문은 거래의 방향성, 진입 및 청산 조건을 명확하게 규정하는 정교한 거래 규칙을 세우는 것과 관련이 있습니다. 세 번째 질문은 조금 더 간단해 보이죠: 어떻게 하면 프로그래밍 언어를 이용하여 매매 거래를 할 수 있을까요?

이번 글에서는 MQL5를 이용한 거래 알고리즘을 어떻게 실제로 실행할 수 있는지에 대해 알아볼게요.


알고리즘 트레이딩을 위한 MQL5 기능

MQL5는 주문, 포지션 및 거래 요청 관련 업무에 필요한 다양한 거래 함수를 갖춘 거래 전략 프로그래밍 언어입니다. 그렇기 때문에 MQL5를 이용하는 것이 개발자에게 가장 손이 덜 가는 트레이딩 로봇 개발 방법이 되죠.

MQL5를 이용하면 거래 요청OrderSend() 함수 또는 OrderSendAsync() 함수를 통해 서버로 보내고, 처리 결과를 받아 보고, 거래 기록을 확인하고, 원하는 심볼의 계약 명세를 살펴보고, 이벤트를 핸들링하고, 그 밖의 필요한 데이터 또한 요청할 수 있습니다.

그 밖에도 기술적 지표를 커스텀하거나, 이미 완성된 지표를 적용하고, 차트에 원하는 마크 또는 객체를 추가하고, 커스텀 UI를 구축하는 등 다양하게 활용할 수 있습니다. 다양한 구현 예제는 관련 문서에서 찾아 보세요.


트레이드 오퍼레이션: 알파벳 배우기만큼 쉬워요!

다음은 트레이딩 로봇에 필요한 기본적인 트레이드 오퍼레이션의 예시입니다:

  1. 현재가로 매수/매도 거래하기,
  2. 정해진 조건에 따른 매수/매도 대기 주문 입력하기,
  3. 대기 주문 정정/취소하기,
  4. 포지션 청산/확대/축소/전환하기.

위의 모든 오퍼레이션은 OrderSend() 함수를 통해 실행 가능합니다. 비동기적으로 처리되는 OrderSendAsync() 함수도 있죠. 모든 트레이드 오퍼레이션은 MqlTradeRequest 구조로 작성되어 있습니다. 따라서, 어려운 일이 있다면 MqlTradeRequest 구조를 올바르게 채우는 것과 요청 실행 결과를 핸들링하는 것 정도가 되겠네요.

매매 프로그램을 이용하면 시장가로 매도 및 매수 주문을 체결할 수 있으며(BUY 또는 SELL), 현재 시장가와는 거리가 다소 떨어져 있는 가격으로 매도 및 매수 대기 주문을 넣을 수 있습니다:

  • BUY STOP, SELL STOP - 지정가 돌파 시 매수 또는 매도(현재가보다 하락한 가격으로 거래);
  • BUY LIMIT, SELL LIMIT - 지정가 도달 시 매수 또는 매도(현재가보다 상승한 가격으로 거래);
  • BUY STOP LIMIT, SELL STOP LIMIT - 지정가 도달 시 스탑-리밋 매수 또는 스탑-리밋 매도

이 같은 기본 주문 유형은 열거형인 ENUM_ORDER_TYPE에 해당합니다. 



대기 주문을 정정하거나 취소해야 하는 경우도 있습니다. 그럴 때는 OrderSend() 또는 OrderSendAsync() 함수를 사용하면 되는데요. 오픈 포지션의 변경 또한 동일한 트레이드 오퍼레이션을 통해 이루어지므로 꽤 쉬운 편입니다.

트레이드 오퍼레이션이 복잡하고 까다롭다고만 생각하셨다면 이제 생각을 바꿀 때입니다. MQL5를 이용한 매매거래 코딩 방법뿐 아니라 거래 계좌 및 심볼 속성의 활용법에 대해서도 알려 드릴게요. 트레이드 클래스도 도움이 될 거예요.


CAccountInfo로 거래 계좌 확인하기

트레이딩 로봇을 개발할 때 가장 처음 생각해야 할 것은 사용하게 될 거래 계좌입니다. 학습 코드를 제작 중인 만큼 한번 엑스퍼트 어드바이저가 실제 계좌에서 열리면 어떤지 확인해 보도록 하죠.

CAccountInfo 클래스는 계좌 관련 작업에 사용됩니다. AccountInfo.mqh 파일을 실행하고 클래스 변수를 account라고 선언하겠습니다:

#include <Trade\AccountInfo.mqh>
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- object for working with the account
   CAccountInfo account;
//--- receiving the account number, the Expert Advisor is launched at
   long login=account.Login();
   Print("Login=",login);
//--- clarifying account type
   ENUM_ACCOUNT_TRADE_MODE account_type=account.TradeMode();
//--- if the account is real, the Expert Advisor is stopped immediately!
   if(account_type==ACCOUNT_TRADE_MODE_REAL)
     {
      MessageBox("Trading on a real account is forbidden, disabling","The Expert Advisor has been launched on a real account!");
      return(-1);
     }
//--- displaying the account type    
   Print("Account type: ",EnumToString(account_type));
//--- clarifying if we can trade on this account
   if(account.TradeAllowed())
      Print("Trading on this account is allowed");
   else
      Print("Trading on this account is forbidden: you may have entered using the Investor password");
//--- clarifying if we can use an Expert Advisor on this account
   if(account.TradeExpert())
      Print("Automated trading on this account is allowed");
   else
      Print("Automated trading using Expert Advisors and scripts on this account is forbidden");
//--- clarifying if the permissible number of orders has been set
   int orders_limit=account.LimitOrders();
   if(orders_limit!=0)Print("Maximum permissible amount of active pending orders: ",orders_limit);
//--- displaying company and server names
   Print(account.Company(),": server ",account.Server());
//--- displaying balance and current profit on the account in the end
   Print("Balance=",account.Balance(),"  Profit=",account.Profit(),"   Equity=",account.Equity());
   Print(__FUNCTION__,"  completed"); //---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }

위의 코드를 통해 확인 가능하듯, OnInit() 함수에서 account 변수를 활용하면 다양하고 유용한 정보를 수신할 수 있어요. 해당 코드를 엑스퍼트 어드바이저에 추가하면 명령어 분석을 통해 로그를 검토할 수 있습니다.

다음은 2012 자동 거래 챔피언십 계좌에 열린 엑스퍼트 에드바이저의 모습입니다.



CSymbolInfo로 심볼 설정 불러오기

이제 계좌에 대한 정보는 있으니, 필요한 트레이드 오퍼레이션을 실행하기 전에 대상 심볼의 속성을 알아봐야 겠죠. CSymbolInfo 클래스는 이럴 때 사용할 수 있는 다양한 메소드를 제공합니다. 아래 예시에서는 아주 일부만 살펴볼 거예요.

#include<Trade\SymbolInfo.mqh>
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- object for receiving symbol settings
   CSymbolInfo symbol_info;
//--- set the name for the appropriate symbol
   symbol_info.Name(_Symbol);
//--- receive current rates and display
   symbol_info.RefreshRates();
   Print(symbol_info.Name()," (",symbol_info.Description(),")",
         "  Bid=",symbol_info.Bid(),"   Ask=",symbol_info.Ask());
//--- receive minimum freeze levels for trade operations
   Print("StopsLevel=",symbol_info.StopsLevel()," pips, FreezeLevel=",
         symbol_info.FreezeLevel()," pips");
//--- receive the number of decimal places and point size
   Print("Digits=",symbol_info.Digits(),
         ", Point=",DoubleToString(symbol_info.Point(),symbol_info.Digits()));
//--- spread info
   Print("SpreadFloat=",symbol_info.SpreadFloat(),", Spread(current)=",
         symbol_info.Spread()," pips");
//--- request order execution type for limitations
   Print("Limitations for trade operations: ",EnumToString(symbol_info.TradeMode()),
         " (",symbol_info.TradeModeDescription(),")");
//--- clarifying trades execution mode
   Print("Trades execution mode: ",EnumToString(symbol_info.TradeExecution()),
         " (",symbol_info.TradeExecutionDescription(),")");
//--- clarifying contracts price calculation method
   Print("Contract price calculation: ",EnumToString(symbol_info.TradeCalcMode()),
         " (",symbol_info.TradeCalcModeDescription(),")");
//--- sizes of contracts
   Print("Standard contract size: ",symbol_info.ContractSize(),
         " (",symbol_info.CurrencyBase(),")");
//--- minimum and maximum volumes in trade operations
   Print("Volume info: LotsMin=",symbol_info.LotsMin(),"  LotsMax=",symbol_info.LotsMax(),
         "  LotsStep=",symbol_info.LotsStep());
//--- 
   Print(__FUNCTION__,"  completed");
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }

아래는 자동 거래 챔피언십의 EUR/USD 통화쌍 속성입니다. 이제 트레이드 오퍼레이션을 실행할 준비가 됐습니다.



CTrade - 트레이드 오퍼레이션을 위한 편리한 클래스

MQL5에서는 OrderSend()와 OrderSendAsync() 함수 두 가지만 알면 거래가 가능합니다. 사실 두 가지 함수라기 구현 방법이 두 가지인 한 가지 함수이죠. OrderSend() 함수는 거래 요청을 보내고 실행 결과를 기다립니다. 비동기적으로 처리되는 OrderSendAsync() 함수는 요청에 대한 트레이딩 서버의 응답을 기다리지 않고 다음 작업을 수행해 나가죠. 하나의 함수만으로 모든 트레이드 오퍼레이션이 가능한 거나 마찬가지니까 MQL5로 거래하는 건 정말 쉽습니다.

그래도 어려운 것도 있겠죠? 두 함수 모두 12개가 넘는 필드를 첫 번째 매개 변수로 갖는 MqlTradeRequest 구조를 수신합니다. 모든 필드를 채울 필요는 없습니다. 필요한 필드 세트는 트레이드 연산 형식에 따라 달라지죠. 필요한 값을 잘못 입력하거나 빈 값으로 두는 경우 오류가 발생하여 서버로 요청이 전달되지 않습니다. 이 가운데 5가지 필드는 미리 정의된 열거형에 포함된 올바른 값을 필요로 하죠.

하나의 거래 요청에 대한 수많은 주문 속성을 규정하기 위해 이렇게 많은 필드가 필요한 건데요. 주문은 실행 정책, 유효 기간 및 기타 매개 변수에 따라 변합니다. 하지만 이 모든 구성 요소를 외울 필요는 없죠. CTrade 클래스를 이용하면 됩니다. 이런 식으로 저희가 제공하는 클래스를 이용해서 트레이딩 로봇을 만들 수 있어요.

#include<Trade\Trade.mqh>
//--- object for performing trade operations
CTrade  trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- set MagicNumber for your orders identification
   int MagicNumber=123456;
   trade.SetExpertMagicNumber(MagicNumber);
//--- set available slippage in points when buying/selling
   int deviation=10;
   trade.SetDeviationInPoints(deviation);
//--- order filling mode, the mode allowed by the server should be used
   trade.SetTypeFilling(ORDER_FILLING_RETURN);
//--- logging mode: it would be better not to declare this method at all, the class will set the best mode on its own
   trade.LogLevel(1); 
//--- what function is to be used for trading: true - OrderSendAsync(), false - OrderSend()
   trade.SetAsyncMode(true);
//---
   return(0);
  }

그럼 CTrade가 트레이드 오퍼레이션에 어떤 도움이 되는지 보도록 할게요.

현재가로 매수/매도 거래하기

매매 전략은 지금 당장 현재가로 매수 또는 매도할 수 있게 해주죠. CTrade를 사용할 경우 거래량만 정하면 됩니다. 나머지 매개 변수(시가 및 심볼, 손절매 및 이익실현 가격, 주석)는 선택적입니다.

//--- 1. example of buying at the current symbol
   if(!trade.Buy(0.1))
     {
      //--- failure message
      Print("Buy() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Buy() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

CTrade는 심볼이 규정되지 않은 경우 자동으로 차트에 있는 심볼을 사용합니다. 간단한 전략을 만들 때 편리하죠. 다중 통화 매매 전략인 경우 트레이드 오퍼레이션 대상 심볼은 항상 명확하게 규정해야 합니다.

//--- 2. example of buying at the specified symbol
   if(!trade.Buy(0.1,"GBPUSD"))
     {
      //--- failure message
      Print("Buy() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Buy() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

손절매 및 이익실현 가격, 시가 및 주석 등의 모든 매개 변수도 규정할 수 있죠.

//--- 3. example of buying at the specified symbol with specified SL and TP
   double volume=0.1;         // specify a trade operation volume
   string symbol="GBPUSD";    //specify the symbol, for which the operation is performed
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // number of decimal places
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // point
   double bid=SymbolInfoDouble(symbol,SYMBOL_BID);             // current price for closing LONG
   double SL=bid-1000*point;                                   // unnormalized SL value
   SL=NormalizeDouble(SL,digits);                              // normalizing Stop Loss
   double TP=bid+1000*point;                                   // unnormalized TP value
   TP=NormalizeDouble(TP,digits);                              // normalizing Take Profit
//--- receive the current open price for LONG positions
   double open_price=SymbolInfoDouble(symbol,SYMBOL_ASK);
   string comment=StringFormat("Buy %s %G lots at %s, SL=%s TP=%s",
                               symbol,volume,
                               DoubleToString(open_price,digits),
                               DoubleToString(SL,digits),
                               DoubleToString(TP,digits));
   if(!trade.Buy(volume,symbol,open_price,SL,TP,comment))
     {
      //--- failure message
      Print("Buy() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("Buy() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

이미 언급한 바와 같이 매직 넘버와 허용되는 슬리피지는 Ctrade를 초기화할 때 설정되었고요. 따라서 필수는 아닙니다. 하지만 필요한 경우 오퍼레이션 실행 전에 설정할 수 있습니다.

리미트오더 설정하기

리미트오더 송신에는 BuyLimit() 또는 SellLimit() 클래스 메소드가 사용됩니다. 대부분의 경우 축약된 버전(시가 및 거래량만 지정)이면 충분해요. 지정가 매수 시가는 현재가보다 낮고, 지정가 매도 시에는 현재가보다 높아야 겠죠. 이러한 주문의 경우 가장 유리한 가격으로 시장에 진입하는 것이 목적이며 가격이 지지선에서 반등할 것으로 예상되는 경우에 사용하는 것이 적합합니다. 엑스퍼트 어드바이저의 심볼은 이럴 때 사용됩니다.

//--- 1. example of placing a Buy Limit pending order
   string symbol="GBPUSD";    // specify the symbol, at which the order is placed
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // number of decimal places
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // point
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // current buy price
   double price=1000*point;                                   // unnormalized open price
   price=NormalizeDouble(price,digits);                       // normalizing open price
//--- everything is ready, sending a Buy Limit pending order to the server
   if(!trade.BuyLimit(0.1,price))
     {
      //--- failure message
      Print("BuyLimit() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("BuyLimit() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

손절매 및 이익실현 가격, 유효 기간, 심볼 및 주석 등 모든 매개 변수를 설정하는, 보다 상세한 버전의 주문도 가능한데요.

//--- 2. example of placing a Buy Limit pending order with all parameters
   double volume=0.1;
   string symbol="GBPUSD";    // specify the symbol, at which the order is placed
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // number of decimal places
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // point
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // current buy price
   double price=1000*point;                                 // unnormalized open price
   price=NormalizeDouble(price,digits);                      // normalizing open price
   int SL_pips=300;                                         // Stop Loss in points
   int TP_pips=500;                                         // Take Profit in points
   double SL=price-SL_pips*point;                           // unnormalized SL value
   SL=NormalizeDouble(SL,digits);                            // normalizing Stop Loss
   double TP=price+TP_pips*point;                           // unnormalized TP value
   TP=NormalizeDouble(TP,digits);                            // normalizing Take Profit
   datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1);
   string comment=StringFormat("Buy Limit %s %G lots at %s, SL=%s TP=%s",
                               symbol,volume,
                               DoubleToString(price,digits),
                               DoubleToString(SL,digits),
                               DoubleToString(TP,digits));
//--- everything is ready, sending a Buy Limit pending order to the server
   if(!trade.BuyLimit(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration,comment))
     {
      //--- failure message
      Print("BuyLimit() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("BuyLimit() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

두 번째 버전으로 주문을 넣는 경우 손절매 및 이익실현 가격을 정확하게 입력하는 것이 중요합니다. 매수 시 이익실현 가격은 시가보다 높아야 하고, 손절매 가격은 시가보다 낮아야 하겠죠. 지정가 매도 시에는 이와 반대로 하면 됩니다. 엑스퍼트 어드바이저에서 과거 데이터로 테스팅을 해보면 쉽게 이해가 될 거예요. 이 경우 CTrade 클래스는 자동으로 메시지를 표시하게 됩니다. LogLevel 함수를 요청한 것이 아니라면요.

스탑오더 설정하기

이와 유사한 방법으로 BuyStop()SellStop() 메소드가 스탑오더 전송에 사용됩니다. 지정가 매수 시 시가는 현재가보다 높고 지정가 매도 시에는 현재가보다 낮아야겠죠. 지정가 주문은 저항선 돌파 시에 시장에 진입하거나 손해를 감수해야 하는 경우에 사용됩니다. 축약된 버전:

//--- 1. example of placing a Buy Stop pending order
   string symbol="USDJPY";    // specify the symbol, at which the order is placed
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // number of decimal places
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // point
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // current buy price
   double price=1000*point;                                    // unnormalized open price
   price=NormalizeDouble(price,digits);                        // normalizing open price
//--- everything is ready, sending a Buy Stop pending order to the server 
   if(!trade.BuyStop(0.1,price))
     {
      //--- failure message
      Print("BuyStop() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("BuyStop() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

좀 더 상세한 버전에서는 지정가 대기 주문에 대한 최대 매개 변수가 모두 설정됩니다.

//--- 2. example of placing a Buy Stop pending order with all parameters
   double volume=0.1;
   string symbol="USDJPY";    // specify the symbol, at which the order is placed
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // number of decimal places
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);         // point
   double ask=SymbolInfoDouble(symbol,SYMBOL_ASK);             // current buy price
   double price=1000*point;                                   // unnormalized open price
   price=NormalizeDouble(price,digits);                       // normalizing open price
   int SL_pips=300;                                          // Stop Loss in points
   int TP_pips=500;                                          // Take Profit in points
   double SL=price-SL_pips*point;                            // unnormalized SL value
   SL=NormalizeDouble(SL,digits);                             // normalizing Stop Loss
   double TP=price+TP_pips*point;                            // unnormalized TP value
   TP=NormalizeDouble(TP,digits);                             // normalizing Take Profit
   datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1);
   string comment=StringFormat("Buy Stop %s %G lots at %s, SL=%s TP=%s",
                               symbol,volume,
                               DoubleToString(price,digits),
                               DoubleToString(SL,digits),
                               DoubleToString(TP,digits));
//--- everything is ready, sending a Buy Stop pending order to the server 
   if(!trade.BuyStop(volume,price,symbol,SL,TP,ORDER_TIME_GTC,expiration,comment))
     {
      //--- failure message
      Print("BuyStop() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("BuyStop() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

적합한 CTrade 클래스 메소드가 지정가 매매 주문에 사용될 수 있죠. 이번에는 가격을 정확하게 입력하는 것이 매우 중요합니다.

포지션 함수

Buy() 또는 Sell() 메소드 대신 포지션 오픈 메소드를 사용할 수도 있지만 더 많은 세부 사항을 입력해야 합니다.

//--- number of decimal places
   int    digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
//--- point value
   double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
//--- receiving a buy price
   double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
//--- calculate and normalize SL and TP levels
   double SL=NormalizeDouble(price-1000*point,digits);
   double TP=NormalizeDouble(price+1000*point,digits);
//--- filling comments
   string comment="Buy "+_Symbol+" 0.1 at "+DoubleToString(price,digits);
//--- everything is ready, trying to open a buy position
   if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,0.1,price,SL,TP,comment))
     {
      //--- failure message
      Print("PositionOpen() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("PositionOpen() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

CTrade 클래스를 사용할 경우에는 심볼만 입력하면 나머지가 자동으로 입력되죠.

//--- closing a position at the current symbol
   if(!trade.PositionClose(_Symbol))
     {
      //--- failure message
      Print("PositionClose() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("PositionClose() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

오픈된 포지션은 손절매 및 이익실현 가격만 수정이 가능한데요. 이는 PositionModify() 메소드를 이용해 진행됩니다.

//--- number of decimal places
   int    digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
//--- point value
   double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
//--- receiving the current Bid price
   double price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
//--- calculate and normalize SL and TP levels
   double SL=NormalizeDouble(price-1000*point,digits);
   double TP=NormalizeDouble(price+1000*point,digits);
//--- everything is ready, trying to modify the buy position
   if(!trade.PositionModify(_Symbol,SL,TP))
     {
      //--- failure message
      Print("Метод PositionModify() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("PositionModify() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

주문 정정 및취소하기

CTrade 클래스의 OrderModify() 메소드는 대기 주문의 매개 변수 수정을 위해 구현되었습니다. 모든 필요 매개 변수는 이 메소드로 제출되어야 하죠.

//--- this is a sample order ticket, it should be received
   ulong ticket=1234556;
//--- this is a sample symbol, it should be received
   string symbol="EURUSD";
//--- number of decimal places
   int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
//--- point value
   double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
//--- receiving a buy price
   double price=SymbolInfoDouble(symbol,SYMBOL_ASK);
//--- calculate and normalize SL and TP levels
//--- they should be calculated based on the order type
   double SL=NormalizeDouble(price-1000*point,digits);
   double TP=NormalizeDouble(price+1000*point,digits);
   //--- setting one day as a lifetime
   datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1);   
//--- everything is ready, trying to modify the order 
   if(!trade.OrderModify(ticket,price,SL,TP,ORDER_TIME_GTC,expiration))
     {
      //--- failure message
      Print("OrderModify() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("OrderModify() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

수정된 주문에 대한 티켓이 발행될 겁니다. 주문 형식에 따라 정확한 손절매 또는 이익실현 가격이 설정되어야 합니다. 새로운 시장가 또한 현재가를 기준으로 적절하게 설정해야 하죠.

주문을 취소하기 위해서는 해당 주문의 티켓을 알고 있어야 합니다.

//--- this is a sample order ticket, it should be received
   ulong ticket=1234556;
//--- everyrging is ready, trying to modify a buy position
   if(!trade.OrderDelete(ticket))
     {
      //--- failure message
      Print("OrderDelete() method failed. Return code=",trade.ResultRetcode(),
            ". Code description: ",trade.ResultRetcodeDescription());
     }
   else
     {
      Print("OrderDelete() method executed successfully. Return code=",trade.ResultRetcode(),
            " (",trade.ResultRetcodeDescription(),")");
     }

CTrade 클래스의 OrderOpen() 다목적 메소드를 사용하면 어떤 형태의 대기 주문이라도 설정할 수 있게 됩니다. 특수하게 고안된 BuyLimit, BuyStop, SellLimit 그리고 SellStop 메소드와는 달리, 더 많은 매개 변수를 명시해야 하는데요. 어쩌면 그게 더 편하실 수도 있어요.


마지막으로 고려할 테크니컬 이슈는 뭘까요?

세 가지 중 두 가지 테크니컬 이슈에 대해 알아봤네요. 여러분의 전략에 맞는 심볼을 정했고 트레이딩 로봇을 이용한 대기 주문 설정 방법 및 매매 주문 설정 방법도 알려 드렸죠. 트레이드 클래스 섹션에서 MQL5 개발자를 위한 유용한 도구를 더 찾으실 수 있을 거예요.

이 클래스들을 활용하면 전략의 테크니컬 이슈는 최소화하면서 거래 자체에 집중할 수 있죠. 또한, CTrade 클래스는 거래 요청 확인에 사용될 수도 있습니다. 약간의 연습을 거치면 클래스를 사용해서 거래 요청 실행 결과 핸들링에 필요한 로직을 갖춘 커스텀 클래스를 제작하실 수 있을 거예요.

마지막 문제는 어떻게 거래 시그널을 받고 또 어떻게 그 시그널을 MQL5로 작성하냐는 것인데요. 알고리즘 트레이딩을 처음 시작하시는 분들은 보통 이동평균 교차를 기반으로 하는 표준 트레이딩 시스템부터 공부를 하시는데요. 표준 시스템 공부를 하려면 먼저 트레이딩 로봇에서 테크니컬 인디케이터를 생성하고 사용할 줄 알아야 합니다.

인디케이터예제->인디케이터 섹션을 처음부터 읽어 보세요. 아주 기초적인 내용부터 아주 복잡한 내용까지 찬찬히 배울 수 있을 거예요. 인디케이터 활용법을 빠르게 한번 훑어보고 싶다면 뉴비들을 위한 MQL5: 엑스퍼트 어드바이저 테크니컬 인디케이터 사용 가이드를 참조하세요.


MQL5로 하는 트레이드 오퍼레이션

복잡한 작업 쉽게 해결하기

어떤 일이라도 처음 마주하게 되는 어려움은 알고 보면 가장 간단하게 해결 가능한 일이기 마련입니다. 숙련된 개발자분들에게 도움이 되는 부분이 있을 수도 있지만 여기에 언급된 트레이딩 알고리즘 개발 방법은 대부분 초보자를 대상으로 작성되었습니다.

MQL5는 알고리즘 트레이딩의 무한한 가능성을 열어줄 뿐 아니라 모두에게 가장 빠르고 쉬운 알고리즘 구현 방법을 제공합니다. 스탠다드 라이브러리의 트레이드 클래스를 이용해 '추세란 무엇이며 실시간 추세는 어떻게 찾나'와 같이 모든 거래자들이 궁금해 하는 내용을 읽으며 더 많은 정보를 더 빠르게 배워 보세요.

조만간 추세를 쫓거나 외국어를 배우는 것보다 MQL5로 트레이딩 로봇을 개발하는 게 훨씬 쉽다는 걸 알게 되실 겁니다!


MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/481

매매 전략 다목적 엑스퍼트 어드바이저 매매 전략 다목적 엑스퍼트 어드바이저
이 글은 대기 주문을 활용하는 전략 및 해당 전략 개발에 사용되는 Metalanguage, 그리고 그 Metalanguage를 기반으로 작동하는 다목적 엑스퍼트 어드바이저에 대한 설명입니다.
MQL5 간단하게 알아보기 MQL5 간단하게 알아보기
매매 전략 프로그래밍 언어인 MQL5를 배우기로 결심은 했는데 아무 것도 모르겠다고요? 초보 투자자의 입장에서 MQL5와 MetaTrader5를 이용해 보고 간단한 소개글을 남깁니다. 이 글은 MQL5를 이용해 할 수 있는 것들에 대한 설명 그리고 MetaEditor5와 MetaTrader5를 사용하는 데에 유용한 팁들을 포함하고 있습니다.
초보자를 위한 간편 스타트 가이드 초보자를 위한 간편 스타트 가이드
여러분, 안녕하세요! 엑스퍼트 어드바이저 생성 방식이나 인디케이터 활용법을 쉽고 빠르게 이해할 수 있도록 돕고자 이번 글을 씁니다. 이 글은 초보자를 대상으로 하며 복잡하거나 난해한 예제는 포함하지 않습니다.
손쉽게 트레이딩 로봇 만들기 손쉽게 트레이딩 로봇 만들기
시장 거래에는 많은 위험이 따릅니다. 그 중에서도 가장 큰 위험은 잘못된 결정을 내리는 것이죠. 투자자라면 누구나 언제든지 작동 가능하고 두려움, 탐욕, 조바심 같은 우리 인간의 약점을 갖지 않는 자신만의 트레이딩 로봇을 꿈꿉니다.