English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
preview
Expert Advisor 개발 기초부터(30부): 차트 트레이드(Chart Trade)를 지표로 사용하시나요?

Expert Advisor 개발 기초부터(30부): 차트 트레이드(Chart Trade)를 지표로 사용하시나요?

MetaTrader 5 | 9 10월 2024, 16:37
50 0
Daniel Jose
Daniel Jose

소개

이전 글인 Expert Advisor 개발 기초부터(29부)에서 우리는 EA에서 차트 트레이드(Chart Trade)을 제거했습니다. 이전에는 EA 성능과 안정성을 개선하기 위해 Volume At Price and Times와 Trade와 같은 다른 방법에도 동일한 작업을 수행했습니다. EA에서 차트 트레이드(Chart Trade)를 제거하면 기본 주문 시스템만 남게 됩니다. 일부 사용자에게는 이 정도로는 부족해 보일 수 있지만 실제로는 EA가 모든 작업을 수행할 수 있습니다. 그러나 시장에서 거래를 시작하고 종료하는 것을 좋아하지만 펜딩 상태로 두는 것을 싫어하고 가격이 특정 수준에 도달할 때까지 기다렸다가 거래를 진입하거나 청산하려는 사람들이 있습니다.

우리가 거래하는 자산과 MetaTrader 5 플랫폼을 사용하면(이 시리즈의 11부에서 설명한 교차 주문 시스템을 사용할 수 있기 때문에 이 부분을 언급합니다) 시장가 주문을 하는 버튼이 있는 퀵 트레이딩에 액세스할 수 있습니다. 퀵 트레이딩은 왼쪽 상단 모서리에서 사용할 수 있습니다. 다음과 같이 표시됩니다:

이 버튼은 기본 차트 트레이딩처럼 작동하지만 교차 주문 시스템에서는 전혀 표시되지 않으므로 사용할 수 없습니다. 따라서 이 경우 우리는 차트 트레이드(Chart Trade)로 돌아가야 합니다. 그러나 차트 트레이드는 더 이상 EA 내부에서 사용되지 않으며 EA 코드의 일부가 아닙니다. 이제부터 차트 트레이드는 단지 지표일 뿐입니다.

차트 트레이딩에서 왜 그렇게 해야 하나요? 그 이유는 EA는 트레이딩 시스템만을 책임져야 하며 이 시스템의 일부가 아닌 모든 것들은 어떻게든 EA 코드에서 제거되어야 하기 때문입니다. 지금은 무의미해 보일 수 있지만 제가 이 부분에 대해 정확한 이유를 설명하는 글을 다음 편에서 준비하고 있으므로 곧 더 명확해질 것입니다.

차트 트레이드를 스크립트로 사용할 수도 있지만 차트 주기를 변경할 때마다 스크립트가 종료되어 수동으로 다시 실행되어야 한다는 단점이 있습니다.

그런 우리가 차트 트레이드를 지표로 사용하는 경우에는 그렇지 않습니다. 그 이유 중 하나는 차트 트레이드가 지표의 실행 스레드에 영향을 미치지 않으므로 EA는 코드에만 집중하고 주문 및 포지션 관리와 관련된 부분에만 집중할 수 있기 때문입니다.

우리는 비록 원래 버전의 모든 컨트롤과 모든 정보를 가지고 있지는 않지만 지표로서의 차트 트레이드는 훨씬 간단하고 여전히 작동합니다. MetaTrader 5 시스템은 기능이 뛰어나면서도 간단합니다. 그러나 우리가 우리의 차트 트레이드(Chart Trade)에 가지게 될 특정 정보에 대한 액세스는 제공하지 않습니다.

이제 시작해보겠습니다. 이 주제는 매우 흥미로울 것입니다.


2.0. 차트 트레이드(Chart Trade) 지표 개발

이 지표를 개발하려면 우리는 꽤 많은 변경을 해야 합니다. 차트 트레이드를 EA 코드의 일부로 만들지 않고 차트에 반환하기 위해 우리는 이러한 변경 사항을 구현할 것입니다.

언뜻 보기에는 아래에 표시된 코드만으로도 차트 트레이드(Chart Trade)를 컴파일할 수 있는 것처럼 보일 수 있습니다. 하지만 본질적으로 꺼두어야 하는 몇 가지 사항과 연결되어 있기 때문입니다. 이러한 부분을 언젠가 다시 EA로 되돌릴 수 있으므로 완전히 삭제하지는 않습니다.

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\SubWindow\C_TemplateChart.mqh>
//+------------------------------------------------------------------+
C_TemplateChart Chart;
C_Terminal                      Terminal;
//+------------------------------------------------------------------+
int OnInit()
{
        Chart.AddThese("IDE(,,170, 240)");

        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+

이 코드를 컴파일 하면 많은 오류가 발생하지만 EA와 호환되는 코드를 유지하기 위해 하나씩 수정하면서 지표로 사용할 수 있도록 조정할 것입니다.

가장 먼저 해야 할 일은 하위 창을 만들거나 관리하는 시스템을 격리하는 것입니다. 차트 트레이드는 이러한 하위 창을 사용하지 않으므로 지표에 이 코드를 내장할 필요가 없습니다. 매우 간단합니다. C_Chart_IDE.mqh를 편집하여 다음과 같이 표시되도록 해야 합니다:

#ifdef def_INTEGRATION_CHART_TRADER
        #include <NanoEA-SIMD\SubWindow\C_SubWindow.mqh>
        #include <NanoEA-SIMD\Trade\Control\C_IndicatorTradeView.mqh>
#else
        #include <NanoEA-SIMD\SubWindow\C_ChartFloating.mqh>
        #include <NanoEA-SIMD\Auxiliar\C_Terminal.mqh>
#endif  
//+------------------------------------------------------------------+
#ifdef def_INTEGRATION_CHART_TRADER
        class C_Chart_IDE : public C_SubWindow
#else 
        class C_Chart_IDE : public C_ChartFloating
#endif 

따라서 차트 트레이드(Chart Trade)와 EA의 주문 표시 시스템 간의 연결을 피하면서 시스템을 하위 창에서 완전히 분리합니다. 이것은 def_INTEGRATION_CHART_TRADER 정의에 의해 제어되지만 이 정의는 EA에서만 사용되므로 그 안의 내용은 지표로 컴파일되지 않습니다.

지표는 하위 창을 사용하지 않으므로 우리는 몇 가지 문제를 해결해야 합니다. 그 중 하나는 아래와 같습니다:

                bool Create(bool bFloat)
                        {
                                m_CountObject = 0;
                                if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
                                FileReadInteger(m_fp, SHORT_VALUE);
                                
                                for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
                                m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA());
                                m_szLine = "";
                                while (m_szLine != "</chart>")

// ... Rest of the code...

그리고 GetIdSubWinEA 함수는 지표가 위치한 창의 번호를 반환합니다. 이 호출은 여러 지점에서 수행됩니다. 이에 대한 두 가지 해결책이 있습니다. 첫 번째 해결책은 호출이 발생하는 각 지점에서 위 함수를 다음과 같이 수정하는 것입니다.

                bool Create(bool bFloat)
                        {
                                m_CountObject = 0;
                                if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
                                FileReadInteger(m_fp, SHORT_VALUE);
                                
                                for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
#ifdef def_INTEGRATION_CHART_TRADER
                                m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA());
#else 
                                m_SubWindow = 0;
#endif 

// ... The rest of the code...

이렇게 하면 문제가 해결되지만 조건부 컴파일 지시어가 너무 많아서 이후 코드를 이해하기 어려워질 정도로 많은 변경을 해야 합니다. 훨씬 간단하지만 똑같이 효과적인 해결책이 있습니다. 정의를 통해 이 호출과 다른 호출을 '에뮬레이션'하기 위해 시스템 코드에 다음 스니펫을 추가하기만 하면 됩니다.

#ifndef def_INTEGRATION_CHART_TRADER
        #define GetIdSubWinEA() 0
        #define ExistSubWin() false
#endif 

이 코드로 호출 문제를 해결할 수 있습니다. 이 정도면 (몇 가지 세부 정보를 추가하면) 지표를 컴파일할 수 있을 것입니다.

두 번째로 큰 문제는 마우스 이벤트와 관련된 함수 즉 마우스 자체가 아니라 C_Mouse 클래스와 관련된 문제입니다.

이해해야 할 점은 차트 트레이드(Chart Trade)다 EA의 일부였을 때 우리는 마우스 위치에 액세스 할 수 있었지만 지표에 C_Mouse 클래스를 추가하기만 하면 지표와 EA간에 이해 상충이 발생한다는 것입니다. 지금은 이 문제가 어떻게 해결되는지 보여드리지 않겠습니다. 모든 문제가 해결되고 기존 차트 트레이드(Chart Trade)의 일부 기능을 확보할 때까지는 일시적으로 다른 방향을 취할 것입니다.

이를 위해 C_Mouse 클래스에서 새 지표로 몇 가지를 이동해야 합니다. 하지만 걱정하지 마세요. 작은 일이니까요. 첫 번째 변경 사항은 C_TemplateChart 클래스에서 발생하며 DispatchMessage 함수를 다음과 같이 변경할 것입니다:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        datetime dt;
        double p;

        C_Chart_IDE::DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
#ifdef def_INTEGRATION_CHART_TRADER
                        Mouse.GetPositionDP(dt, p);
#else
                        {
                                int w;
                                ChartXYToTimePrice(Terminal.Get_ID(), (int)lparam, (int)dparam, w, dt, p);
                        }
#endif 
                        for (int c0 = 0; c0 < m_Counter; c0++)  if (m_Info[c0].szVLine != "")

// ... Rest of the code...

강조 표시된 섹션을 추가하면 C_Mouse 클래스가 여전히 존재하는 것처럼 기능을 사용할 수 있습니다. 다음 변경 사항은 앞서 만든 것과 비슷한 것을 만드는 C_ChartFloating 클래스입니다. 하지만 코드는 조금 다릅니다:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        int mx, my;
        datetime dt;
        double p;
        static int six = -1, siy = -1, sic = -1;
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
#ifdef def_INTEGRATION_CHART_TRADER
                        Mouse.GetPositionXY(mx, my);
                        if ((Mouse.GetButtonStatus() & 0x01) == 1)
#else 
                        mx = (int)lparam;
                        my = (int)dparam;
                        if (((uint)sparam & 0x01) == 1)
#endif 
                        {
                                if (sic == -1)  for (int c0 = m_MaxCounter - 1; (sic < 0) && (c0 >= 0); c0--)

// ... Rest of the code...

이제 지표를 컴파일하면 아래 동영상에 표시된 결과를 얻을 수 있습니다:



EA가 아직 완전한 기능을 갖추지는 못했지만 차트 트레이드(Chart Trade)가 EA와 동일한 기능을 갖거나 이전과 현재 MetaTrader 5에서 제공되는 기능의 중간 정도로 축소될 수 있습니다. 제 입장은 상당히 급진적이기 때문에 EA에 있던 기능과 거의 동일한 기능으로 차트 트레이드가 기능하도록 하려고 합니다. 여러분은 원하는 경우 기능을 줄일 수 있습니다.

이제 차트 트레이드(Chart Trade)가 지표가 되었으니 다음 주제로 넘어가겠습니다.


2.1. 차트 트레이드(Chart Trade) 지표를 작동시키는 방법

이제 상황이 더 어려워질 것입니다. 이제 주문을 보내고 포지션을 청산하고 거래 결과를 보고할 수 있도록 이 지표를 작동시키는 방법을 살펴보겠습니다. 사실 MetaTrader 5 플랫폼은 우리가 적은 노력만 들여도 작동할 수 있는 여러 방법을 제공하기 때문에 언뜻보기에 이것은 어렵지 않습니다.

여기서는 MetaTrader 5의 일부 기능을 사용하여 내부 클라이언트-서버 시스템(플랫폼 내부)을 만들어 서로 다른 프로세스 간에 데이터를 전송한 파트 16과 동일한 작업을 수행합니다. 양방향 커뮤니케이션이 필요하고 사용자에게 보이지 않아야 하기 때문에 시뮬레이션만 약간 다를 뿐 비슷한 작업을 수행합니다.

이 방법만이 가능한 것은 아닙니다. 이 전송을 활성화하기 위해 DLL을 사용하는 방법도 있습니다. 그러나 우리는 필요에 따라 탐색, 유지 관리 및 수정하기 가장 쉬운 MetaTrader 5 변수를 사용할 것입니다.

이 길을 택하기로 결정했으니 이제 두 번째로 중요한 결정이 남았습니다. 누가 서버가 되고 누가 클라이언트가 될 것인가 하는 문제입니다. 이 결정은 시스템이 실제로 구현되는 방식에 영향을 미칩니다. 어쨌든 우리는 이미 아래와 같이 구현된 메시징 프로토콜을 가지고 있을 것입니다:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableLeverage      (_Symbol + "_Leverage")
#define def_GlobalVariableTake          (_Symbol + "_Take")
#define def_GlobalVariableStop          (_Symbol + "_Stop")
#define def_GlobalVariableResult        (_Symbol + "_Result")
#define def_GlobalVariableButton        (_Symbol + "_ButtonState")
//+------------------------------------------------------------------+
#define def_ButtonDTSelect              0x01
#define def_ButtonSWSelect              0x02
#define def_ButtonBuyMarket             0x04
#define def_ButtonSellMarket            0x08
#define def_ButtonClosePosition         0x10
//+------------------------------------------------------------------+

여기까지 입니다. 메시지 프로토콜을 이미 정의했기 때문에 누가 클라이언트가 될지, 누가 서버가 될지 결정할 필요가 없습니다. 이 프로토콜은 처음부터 정의해야 다른 모든 것을 더 쉽게 개발할 수 있도록 하기 때문에 반드시 정의되어야 합니다.

차트 트레이드(Chart Trade) EA 세트가 있는 각 자산에 대해 5개의 변수를 사용한다는 점에 유의하세요. 변수 이름은 세트가 연결된 자산에 따라 달라지므로 여러 자산에 동시에 세트를 사용할 수 있습니다.

여기서 중요한 질문이 나옵니다. 서버의 역할은 누가 할 것일까요? 서버의 역할이 이러한 변수의 생성을 담당합니다. 개인적으로는 EA를 서버로 사용하고 차트 트레이드(Chart Trade)는 클라이언트로 두는 것이 더 실용적이라고 생각합니다. 이 아이디어는 항상 차트에 EA를 두고 필요할 때 특정 시간에 차트 트레이드(Chart Trade)를 사용하는 것입니다. 따라서 EA는 5개 변수 중 4개 변수를 생성할 책임이 있는데 그 중 하나가 어떤 버튼을 눌렀는지 알려주는 역할을 하기 때문입니다. 이것이 바로 차트 트레이드(Chart Trade)의 역할입니다.

이 모든 것을 기반으로 데이터 흐름은 다음과 같습니다:

  1. EA는 레버리지, 이익 실현 및 손절매를 나타내는 초기 값을 포함하는 글로벌 변수를 생성합니다. 결국 차트 트레이드(Chart Trade)가 사용자에게 결과를 보여줄 수 있고 다른 곳에서 이 정보를 찾을 필요가 없도록 당일 결과를 알려주는 변수도 생성합니다.
  2. 차트 트레이드(Chart Trade)는 버튼을 누른 값을 나타내는 변수를 생성합니다. 이 변수는 오픈 포지션 청산 또는 시장가 매수 또는 매도 실행과 같은 작업을 EA에 지시합니다. 이 변수는 이 기간 동안에만 존재하며 EA가 요청을 완료하는 즉시 사라집니다.

흐름은 간단하지만 이전과 마찬가지로 차트 트레이드(Chart Trade)는 시스템이 주문을 보내고 포지션을 청산하도록 하며 EA와 상호 작용할 수 있도록 보장합니다.

EA가 있을 때만 차트 트레이드(Chart Trade)가 차트에 존재하도록 하려면 몇 가지 확인 사항을 추가해야 합니다. EA가 주문을 전송할 수 없는 경우 차트 트레이드(Chart Trade)를 차트에 표시하는 것은 의미가 없습니다. 이러한 확인에는 다음 두 가지 경우가 포함됩니다:

  • 첫 번째는 지표가 초기화될 때 수행됩니다.
  •  두 번째는 차트에서 이벤트가 발생할 때 수행됩니다.

코드에서 이를 확인하면 프로세스를 더 쉽게 이해할 수 있습니다. 초기화하는 동안 다음 코드가 실행됩니다:

#define def_SHORTNAME "CHART TRADE"
//+------------------------------------------------------------------+
int OnInit()
{
        long lparam = 0;
        double dparam = 0.0;
        string sparam = "";
        
        IndicatorSetString(INDICATOR_SHORTNAME, def_SHORTNAME);
        if(!GlobalVariableGet(def_GlobalVariableLeverage, dparam)) return INIT_FAILED;
        Terminal.Init();
        Chart.AddThese("IDE(,,170, 215)");
        Chart.InitilizeChartTrade(dparam * Terminal.GetVolumeMinimal(), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), true);
        OnChartEvent(CHARTEVENT_OBJECT_ENDEDIT, lparam, dparam, sparam);

        return INIT_SUCCEEDED;
}

강조 표시된 선은 글로벌 변수 중 하나(이 경우 레버리지 수준을 나타내는 변수)를 평가합니다. 이 변수가 누락되면 초기화에 실패합니다. 이 변수를 생성하는 것은 차트 트레이드(Chart Trade)가 아니라 EA이므로 이렇게 하면 지표는 차트에 EA가 있는지 여부를 알 수 있고 EA가 작업을 완료하면 MetaTrader 5에서 이 변수를 제거하여 차트 트레이드(Chart Trade)도 강제로 삭제된다는 점을 기억하세요. 두 번째 지점에서는 글로벌 레버리지 변수를 사용할 수 있는지 여부라는 동일한 조건을 확인합니다.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED);
        Chart.DispatchMessage(id, lparam, dparam, sparam);
}

위에 표시된 사항은 비교적 높은 빈도로 구현됩니다. 모든 지표가 동일한 운영 스레드를 사용하므로 매우 신중하게 처리하지 않는 경우 한 지표에 온타임 이벤트 시스템을 배치하면 다른 모든 지표에 영향을 미치므로 바람직하지 않습니다.

위의 검사가 플랫폼의 일반적인 성능을 방해한다고 생각되면 OnTime 이벤트 안에 배치할 수 있지만 결과에 유의하세요.

누가 차트에서 차트 트레이드(Chart Trade) 지표를 제거하든 상관없이 아래에서 볼 수 있듯이 같은 시점에 제거됩니다:

void OnDeinit(const int reason) 
{ 
        if (reason == REASON_INITFAILED)
        {
                Print("Unable to use Chart Trade. The EA is not on the chart of this asset...");
                if (!ChartIndicatorDelete(0, 0, def_SHORTNAME))
                        Print("Unable to delete Chart Trade from the chart.");
        }
}

선택한 선은 차트에서 지표를 제거합니다. 실패할 경우 관련 메시지가 표시됩니다. 이러한 메시지는 아래와 같이 도구 상자에서 확인할 수 있습니다:

따라서 여러분은 이 창에 있는 모든 정보를 항상 숙지하고 있어야 합니다.

C_Chart_IDE 클래스의 변경 사항에 대한 심층적인 분석으로 넘어가기 전에 마지막으로 한 가지 누락된 함수가 있습니다. 이 함수는 다음과 같습니다:

int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        double value;
        
        if (GlobalVariableGet(def_GlobalVariableResult, value))
        {
                GlobalVariableDel(def_GlobalVariableResult);
                Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, value, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eROOF_DIARY]);
        }
   
        return rates_total;
}

이 함수는 글로벌 변수를 감시하는 역할을 합니다. 따라서 때때로 포지션을 청산할 때 EA는 글로벌 변수를 생성하며 이 변수의 이름은 def_GlobalVariableResult에 정의되어 있습니다. 이 변수가 생성되고 그 이름이 차트 트레이드(Chart Trade)에서 관찰한 것과 일치하면 이 변수의 값이 캡처 되고 변수는 즉시 삭제됩니다. 이는 처리가 완료되기 전에 EA가 업데이트를 전송하게 되면 이 업데이트가 손실되는 상황 발생하게 되므로 이를 방지하기 위한 것입니다. 그러나 삭제하기 전에 값을 캡처하면 메시지 처리를 담당하는 차트 트레이드 클래스로 전송하여 EA가 전달한 값이 가능한 한 빨리 차트 트레이드에 표시되도록 할 수 있습니다.

이것으로 차트 트레이드(Chart Trade) 지표를 작동시키는 기초가 되는 부분이 완료되었습니다. 두 번째 부분은 버튼에 관한 것입니다. 우리는 기능적인 측면도 고려해야 합니다. 이 작업은 C_Chart_IDE 함수의 메시지 처리 함수에서 쉽게 수행될 수 있습니다:

// ... Previous code ...

case CHARTEVENT_OBJECT_CLICK:
        if (StringSubstr(sparam, 0, StringLen(def_HeaderMSG)) != def_HeaderMSG)
	{
		Resize(-1);
		return;
	}
	sparam = StringSubstr(sparam, 9, StringLen(sparam));
	StringToUpper(sparam);
#ifdef def_INTEGRATION_CHART_TRADER
	if ((sparam == szMsgIDE[eBTN_SELL]) || (sparam == szMsgIDE[eBTN_BUY]))
		TradeView.ExecuteOrderInMarket(m_BaseFinance.Leverange, m_BaseFinance.FinanceTake, m_BaseFinance.FinanceStop, sparam == szMsgIDE[eBTN_BUY], m_BaseFinance.IsDayTrade);
	if (sparam == szMsgIDE[eBTN_CANCEL])
	{
		TradeView.CloseAllsPosition();
		ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false);
	}
#else 
	{
		union u00
		{
			double Value;
			ulong c;
		}u_local;
                                                        
		u_local.c = 0;
		if (sparam == szMsgIDE[eBTN_BUY]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonBuyMarket; else
		if (sparam == szMsgIDE[eBTN_SELL]) u_local.c = (m_BaseFinance.IsDayTrade ? def_ButtonDTSelect : def_ButtonSWSelect) + def_ButtonSellMarket; else
		if (sparam == szMsgIDE[eBTN_CANCEL])
		{
			u_local.c = def_ButtonClosePosition;
			ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false);
		}
                if (u_local.Value > 0) GlobalVariableSet(def_GlobalVariableButton, u_local.Value);
	}
#endif 
	if (sparam == szMsgIDE[eCHECK_DAYTRADE]) InitilizeChartTrade(0, 0, 0, m_BaseFinance.IsDayTrade ? false : true);
	break;

//... Rest of the code...

이 조각에는 두 개의 코드가 포함되어 있습니다. 파란색 코드는 차트 트레이드(Chart Trade)가 EA에 내장된 경우에 사용됩니다. 녹색은 차트 트레이드(Chart Trade)가 지표로 있을 때 사용됩니다.

우리는 녹색 코드를 살펴보아야 합니다. 이 코드는 글로벌 변수를 생성하고 버튼 상태를 설정합니다. 따라서 트레이더가 포지션을 청산하면 청산 버튼에 해당하는 값이 변수에 배치됩니다. 그러나 만약 여러분이 시장가 주문을 하면 이 값은 달라지며 이제 두 가지 다른 값의 조합이 됩니다. 하나는 매수 또는 매도 주문을 나타내고 다른 하나는 당일 거래 또는 장기 거래를 원하는지 여부를 나타냅니다. 이것이 차트 트레이드(Chart Trade)가 EA에 알려주는 전부입니다.

중요: 즉 매도 버튼을 클릭한 후 EA가 무엇이든 수행하기 전에 매수 버튼을 클릭하면 새로운 매수 값으로 인해 매도가 없어지므로 EA는 매수를 하게 됩니다. 또한 이미 오픈 포지션이 있는 상태에서 매도 또는 매수를 요청하고 EA가 관련 거래를 하기 전에 취소를 누르면 포지션이 청산됩니다.

이제 EA 코드로 이동하여 새로운 모델에서 어떻게 작동하는지 살펴보겠습니다.


2.2. 차트 트레이드로부터 메시지를 수신하도록 EA 코드 수정하기

이 부분은 몇 가지 작은 세부 사항만 조정하면 되므로 매우 쉽습니다. 단 다음 사항을 기억하세요: EA 및 차트 트레이드(Chart Trade)를 로드한 후에는 EA에 포함된 글로벌 변수나 데이터를 변경하지 마세요. 주문 시스템을 사용하거나 차트 트레이드(Chart Trade) 자체를 사용하지 않으면 문제가 발생할 수 있습니다. 어떤 문제에는 해결책이 있지만 어떤 문제에는 해결책이 없습니다. 따라서 만일을 대비해 사용 가능한 도구를 사용하되 여러분의 삶을 복잡하게 만들려고 하지 마세요. 

이전 글 (파트 29)에서 차트 트레이드 제거를 하면서 우리는 몇 가지 변경 사항을 적용했는데 그 중 일부가 취소될 예정입니다. 이 질문과 관련된 다른 사항은 변경할 필요가 없습니다. 그러나 앞서 언급했듯이 어떤 것은 고칠 수 있고 어떤 것은 고칠 수 없으므로 이 기사의 다음 주제에서는 차트 트레이드와 EA 간의 관계에서 몇 가지 작은 문제를 제거할 것입니다.

먼저 EA와 차트 트레이드(Chart Trade) 간에 어느 정도의 통신이 가능하도록 EA에서 실행 취소하고 활성화해야 할 사항들을 살펴보겠습니다.

먼저 다음을 변경해 보겠습니다:

input int       user20      = 1;        //Leverage
input double    user21      = 100;      //Take Profit
input double    user22      = 81.74;    //Stop Loss 
input bool      EA_user23   = true;     //Day Trade ?

EA가 롱 또는 숏 거래를 선호할지 여부를 나타내는 값은 변경되지 않습니다. 이 작업은 차트 트레이드(Chart Trade) 또는 펜딩 주문에서 수행해야 합니다. 이전 글에서 이미 이 작업을 수행하는 방법을 보여드렸으니 이제 코딩 작업을 시작하겠습니다. OnInit 이벤트에 다음과 같은 변경 사항을 추가해 보겠습니다:

int OnInit()
{
        if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        {
                Sound.PlayAlert(C_Sounds::TRADE_ALLOWED);
                return INIT_FAILED;
        }
        
        Terminal.Init();

#ifdef def_INTEGRATION_TAPE_READING
        VolumeAtPrice.Init(user32, user33, user30, user31);
        TimesAndTrade.Init(user41);
        EventSetTimer(1);
#endif 

        Mouse.Init(user50, user51, user52);
        
#ifdef def_INTEGRATION_CHART_TRADER
        static string   memSzUser01 = "";
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(EA_user20 * Terminal.GetVolumeMinimal(), EA_user21, EA_user22, EA_user23);
        TradeView.Initilize();
   OnTrade();
#else
        GlobalVariableTemp(def_GlobalVariableLeverage);
        GlobalVariableTemp(def_GlobalVariableTake);
        GlobalVariableTemp(def_GlobalVariableStop);
        GlobalVariableTemp(def_GlobalVariableResult);
        GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal());
        GlobalVariableSet(def_GlobalVariableTake, user21);
        GlobalVariableSet(def_GlobalVariableStop, user22);
        TradeView.Initilize();
        GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof());
#endif 
   
        return INIT_SUCCEEDED;
}

보시다시피 여기에 통신에 사용될 글로벌 변수를 추가합니다. 이미 언급했듯이 EA는 항상 차트 트레이드 전에 시작해야 하며 그렇지 않으면 지표를 초기화할 수 없습니다. 차트 트레이드(Chart Trade)에서 처음에 사용될 값은 EA에 지정되어 있습니다. 이전에 거래가 있었더라도 초기화가 완료됩니다: 누적된 가치도 다시 차트 트레이드로 전송됩니다.

중요한 세부 사항에 주의하세요: 생성된 변수는 임시 유형입니다. EA 데이터 덤프의 경우 특정 시간이 지나면 사용할 수 없으므로 우리는 이러한 변수를 저장하지 않으려는 것입니다. 갑자기 플랫폼이 종료되더라도 이러한 변수는 더 이상 존재하지 않습니다.

구현할 또 다른 추가 사항은 아래와 같습니다:

void OnDeinit(const int reason)
{
        Mouse.Destroy();
        TradeView.Finish();
#ifndef def_INTEGRATION_CHART_TRADER
        GlobalVariableDel(def_GlobalVariableLeverage);
        GlobalVariableDel(def_GlobalVariableTake);
        GlobalVariableDel(def_GlobalVariableStop);
        GlobalVariableDel(def_GlobalVariableResult);
        GlobalVariableDel(def_GlobalVariableButton);
#endif
#ifdef def_INTEGRATION_TAPE_READING
        EventKillTimer();
#endif 
}

변수는 일시적인 것이지만 우리는 변수를 강제로 제거하도록 EA에 요청합니다. 이렇게 하면 차트 트레이드(Chart Trade)는 더 이상 차트에 남아있지 않게 됩니다. 이 OnDeinit 이벤트에는 작은 문제가 있지만 이에 대해서는 다음 주제에서 다루겠습니다. 이제 다소 흥미로운 또 다른 점을 살펴보겠습니다. 이 작업은 두 가지 방법으로 수행할 수 있지만 결과는 거의 동일합니다. 거의 동일하다고 말하는 이유는 차이를 만들 수 있는 사소한 차이점이 있기 때문입니다. (말장난 죄송합니다)

차트 트레이드에는 몇 가지 버튼이 있으며 이 버튼들은 글로벌 변수를 통해 EA에 메시지를 보냅니다. 다음은 이러한 클릭에 적절히 대응하는 EA 내부의 함수입니다:

inline void ChartTrade_ClickButton(void)
{
        union u00
        {
                double Value;
                ulong c;
        }u_local;
        
        if (GlobalVariableGet(def_GlobalVariableButton, u_local.Value))
        {
                GlobalVariableDel(def_GlobalVariableButton);
                                
                if (u_local.c == def_ButtonClosePosition) TradeView.CloseAllsPosition(); else
                        TradeView.ExecuteOrderInMarket(GlobalVariableGet(def_GlobalVariableLeverage), GlobalVariableGet(def_GlobalVariableTake), GlobalVariableGet(def_GlobalVariableStop), ((u_local.c & def_ButtonBuyMarket) == def_ButtonBuyMarket), ((u_local.c & def_ButtonDTSelect) == def_ButtonDTSelect));
                TradeView.Initilize();
        }
}

함수는 다음과 같이 선언됩니다. inline즉 함수는 컴파일러에 의해 선언된 위치에 배치되어야 합니다. 이렇게 하면 가능한 한 빨리 실행됩니다.

매우 중요한 세부 사항: 우리는 이 함수를 어디에 배치해야 할까요? 생각해 봅시다. 여기에 체크하는 것이 있으므로 항상 실행되지는 않습니다. 따라서 우리에게는 두 가지 가능성이 있습니다. 첫 번째는 OnTick 이벤트 안에 함수를 배치하는 것이고 두 번째는 OnTime 이벤트 안에 함수를 배치하는 것입니다. 선택은 특정 논리를 기반으로 되어야 하며 그렇지 않으면 문제가 발생할 수 있습니다.

생각해 봅시다: 이 함수를 OnTick 이벤트에 넣으면 트레이딩 서버에서 플랫폼으로 들어오는 모든 새로운 틱에 대해 실행됩니다. 이 함수는 여러 번 실행되지 않고 특정 시간에만 실행되므로 좋은 해결책으로 보입니다. 하지만 이로 인해 문제가 발생합니다: 거래하는 자산의 변동성이 매우 낮은 경우 OnTick 이벤트의 빈도도 매우 낮아지며 이는 차트 트레이드(Chart Trade) 클릭으로 이벤트가 트리거되는 데 문제가 발생할 수 있음을 의미합니다.

두 번째 옵션은 다음과 같습니다. 함수를 온타임에 배치하면 온타임 이벤트가 일정한 주기로 트리거되면서 함수가 실행될 것입니다. 하지만 이렇게 하면 글로벌 변수를 관찰하는 것 외에는 더 이상 OnTime 이벤트를 다른 용도로 사용할 수 없게 된다는 점을 기억해야 합니다.=.

이 단계에서는 EA가 시장을 지켜볼 것이기 때문에 우리는 아주 좋은 선택을 한 것입니다. 앞으로 몇 개의 글을 통해 이러한 사실이 점점 더 분명해질 것입니다. 그런 다음 OnTime 이벤트 안에 함수를 넣는 것이 좋습니다. 하지만 다음과 같은 질문이 생길 것입니다: 온타임 이벤트가 매초마다만 실행되지 않나요?

실제로는 1초에 한 번 실행되는 경우가 대부분이지만 EventSetMillisecondTimer 함수를 사용하여 더 짧은 주기를 설정할 수 있습니다. 따라서 dnflsms 1초 이내에 이벤트를 실행할 수 있습니다. 저는 대부분의 경우 500ms면 충분하다고 생각합니다. 그러므로 EA의 OnInit 이벤트에 다음과 같은 줄을 추가할 것입니다:

#else
        GlobalVariableTemp(def_GlobalVariableLeverage);
        GlobalVariableTemp(def_GlobalVariableTake);
        GlobalVariableTemp(def_GlobalVariableStop);
        GlobalVariableTemp(def_GlobalVariableResult);
        GlobalVariableSet(def_GlobalVariableLeverage, user20 * Terminal.GetVolumeMinimal());
        GlobalVariableSet(def_GlobalVariableTake, user21);
        GlobalVariableSet(def_GlobalVariableStop, user22);
        TradeView.Initilize();
        GlobalVariableSet(def_GlobalVariableResult, TradeView.GetFinanceRoof());
        EventSetMillisecondTimer(500);
#endif 

이벤트에 다음 줄을 추가하기만 하면 되는 OnDeinit 함수에서 이 이벤트를 중지하는 것을 잊지 마세요:

void OnDeinit(const int reason)
{
        EventKillTimer();

이제 온타임 이벤트가 어떤 모습인지 살펴보겠습니다. 이 이벤트는 강조 표시된 줄만 실제로 컴파일되는 매우 간단한 코드입니다.

void OnTimer()
{
#ifndef def_INTEGRATION_CHART_TRADER
        ChartTrade_ClickButton();
#endif

#ifdef def_INTEGRATION_TAPE_READING
        VolumeAtPrice.Update();
        TimesAndTrade.Connect();
#endif 
}

이게 전부일까요? 아니요, 작지만 또 다른 문제가 있습니다. EA의 초기화 변수를 변경하고 차트에 새로운 펜딩 주문이 들어올 때마다 우리는 차트 트레이드(Chart Trade)가 데이터를 검색하게 하고 싶다는 것을 기억해 봅시다. 이를 위해 우리는 C_IndicatorTradeView 클래스 코드에 작은 세부 사항을 하나 추가해야 합니다. 이 코드는 다음과 같이 표시됩니다:

        case CHARTEVENT_MOUSE_MOVE:
                Mouse.GetPositionDP(dt, price);
                mKeys   = Mouse.GetButtonStatus();
                bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse button click
                bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT pressed
                bKeySell = (mKeys & 0x08) == 0x08;    //CTRL pressed
                if (bKeyBuy != bKeySell)
                {
                        if (!bMounting)
                        {
#ifdef def_INTEGRATION_CHART_TRADER
                                m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
#else 
                                m_Selection.vol = GlobalVariableGet(def_GlobalVariableLeverage) * Terminal.GetVolumeMinimal();
                                valueTp = GlobalVariableGet(def_GlobalVariableTake);
                                valueSl = GlobalVariableGet(def_GlobalVariableStop);
                                m_Selection.bIsDayTrade = EA_user23;
#endif 

강조 표시된 코드는 이제 글로벌 변수에 있는 값을 캡처하므로 차트 트레이드(Chart Trade)에 있는 모든 값이 주문 시스템에 입력됩니다. 유일한 세부 사항은 모든 펜딩 주문이 EA가 지정한 시간을 따르지만 차트에서 직접 변경될 수 있다는 것입니다. 자세한 내용은 차트 트레이드(Chart Trade)를 거치지 않고 차트에서 직접 펜딩 주문을 수정하는 방법을 설명한 ' EA 개발하기 기초부터(파트 27)'을 참조하세요.

사운드 시스템에도 약간의 수정이 있습니다. 차트 트레이드(Chart Trade)를 통해 작업하는 경우 포지션 진입 및 청산시의 알림을 위해 C_Router 클래스에 사운드가 추가되었습니다. 우리는 사운드 시스템에서 무언가를 제거하거나 변경하려는 경우 고려해야 할 다른 사항이 있다는 점을 기억해야 합니다.

마무리하기 전에 OnDeinit 이벤트에 우리가 수정하려고 했던 문제가 있다는 것을 기억하시는지요. 다음 주제로 넘어가서 이 문제를 해결해 보겠습니다.


2.3. 차트 트레이드(Chart Trade)가 조기에 종료되지 않도록 하기

차트 주기 또는 EA 매개변수 변경과 같은 작업을 수행하면(무엇이든 가능) MetaTrader 5는 OnDeinit 이벤트를 생성합니다. 이 이벤트가 트리거되면 모든 것이 정상적으로 작동하는지 확인하기 위해 상황을 다시 분석해야 합니다.

대부분의 경우 이 이벤트가 트리거 되어도 문제가 발생하지 않습니다. 하지만 우리는 클라이언트-서버 시스템을 만들고 글로벌 변수를 사용하여 서버(EA)의 실행이 중지되었는지를 확인하기 때문에 특정 상황을 우회하는 방법을 이해해야 합니다.

원래 OnDeinit 이벤트를 처리하는 함수는 다음과 같습니다:

void OnDeinit(const int reason)
{
        EventKillTimer();
        Mouse.Destroy();
        TradeView.Finish();
#ifndef def_INTEGRATION_CHART_TRADER
        GlobalVariableDel(def_GlobalVariableLeverage);
        GlobalVariableDel(def_GlobalVariableTake);
        GlobalVariableDel(def_GlobalVariableStop);
        GlobalVariableDel(def_GlobalVariableResult);
        GlobalVariableDel(def_GlobalVariableButton);
#endif
#ifdef def_INTEGRATION_TAPE_READING
        EventKillTimer();
#endif 
}

강조 표시된 선은 차트 트레이드(Chart Trade) 지표에서 EA가 차트에 있는지 여부를 확인하는 데 사용하는 글로벌 변수를 제거합니다. 이 검사는 다음 코드에서 수행됩니다:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        if (!GlobalVariableCheck(def_GlobalVariableLeverage)) OnDeinit(REASON_INITFAILED);
        Chart.DispatchMessage(id, lparam, dparam, sparam);
}

즉 EA에서 OnDeinit 이벤트를 활성화하는 일이 발생할 때마다 이 변수가 삭제됩니다. 그리고 EA가 변수를 다시 만들기 전에 지표가 차트에서 이미 제거되었을 수 있으며 이는 정말 이상한 일이 아닐 수 없습니다. 이러한 현상이 어떤 시점에서는 발생하고 어떤 시점에서는 발생하지 않을 것입니다. 우리는 어떻게든 모든 것이 예상대로 작동하도록 해야 합니다.

저는 문서에서 이 문제에 대한 해결책을 찾았습니다. 이는 초기화 해제 이유 코드 섹션에서 확인할 수 있습니다. 이 코드를 살펴보면 차트에서 EA가 제거될 때까지 변수가 삭제되지 않도록 OnDeinit 이벤트 처리 함수를 구성할 수 있습니다.

따라서 솔루션과 새로운 처리 코드는 다음과 같습니다:

void OnDeinit(const int reason)
{
        EventKillTimer();
        Mouse.Destroy();
        TradeView.Finish();
#ifndef def_INTEGRATION_CHART_TRADER
        switch (reason)
        {
                case REASON_CHARTCHANGE:
                        break;
                default:                
                        GlobalVariableDel(def_GlobalVariableLeverage);
                        GlobalVariableDel(def_GlobalVariableTake);
                        GlobalVariableDel(def_GlobalVariableStop);
                        GlobalVariableDel(def_GlobalVariableResult);
                        GlobalVariableDel(def_GlobalVariableButton);
        };
#endif
#ifdef def_INTEGRATION_TAPE_READING
        EventKillTimer();
#endif 
}

이제 차트 차트주기 또는 플로팅 방식만 변경하면 차트에서 차트 트레이드(Chart Trade) 지표가 사라지는 불편함이 더 이상 없을 것입니다. 우리는 다른 상황에서도 모든 것이 잘 될 것이라는 실질적인 확신이 없기 때문에 차트에서 삭제될 수도 있습니다. 따라서 EA와 차트 트레이드(Chart Trade)가 로드된 후에는 더 이상 EA 매개변수를 수정해서는 안됩니다.


결론

약간의 창의력이 어떤 결과를 가져올 수 있는지 보셨나요? 우리는 때로는 해결할 수 없을 것 같은 문제를 해결해야 할 때도 있습니다. 하지만 문서를 통해 공부하면 해결책을 찾을 수 있으므로 아이디어를 실행에 옮기기 위해서는 항상 문서를 확인하고 이해하는 것이 필수적입니다.


MetaQuotes 소프트웨어 사를 통해 포르투갈어가 번역됨
원본 기고글: https://www.mql5.com/pt/articles/10653

파일 첨부됨 |
EA_-_p_Parte_30_u.zip (14532.23 KB)
새로운 기능: MQL5의 커스텀 인디케이터 새로운 기능: MQL5의 커스텀 인디케이터
MetaTrader5와 MQL5의 새로운 기능 전체를 나열하지는 않겠습니다. 종류도 많은 데다가, 별도의 설명이 필요한 기능들도 있거든요. 객체 지향 프로그래밍을 이용한 코드 작성법 또한 다음에 알아보도록 하겠습니다. 다른 기능들과 함께 설명하기에는 조금 어려운 이야기일 수 있으니까요. 이 글에서는 인디케이터와 인디케이터의 구조, 드로잉 타입과 프로그래밍 디테일을 MQL4와 비교해 볼게요. 초보자 분들께 많은 도움이 되면 좋겠고 기존에 사용하시던 개발자 분들도 뭔가 새로운 걸 얻어 가실 수 있길 바랍니다.
Expert Advisor 개발 기초부터(29부): 대화형 플랫폼 Expert Advisor 개발 기초부터(29부): 대화형 플랫폼
이 글에서는 MetaTrader 5 플랫폼이 말을 하도록 하는 방법에 대해 알아보겠습니다. EA를 더 재미있게 만들면 어떨까요? 금융 시장 트레이딩은 너무 지루하고 단조로운 경우가 많지만 우리는 덜 지루하게 만들 수 있습니다. 이 프로젝트는 중독 증세가 있는 사람들에게는 위험할 수 있다는 점에 유의하세요. 하지만 일반적인 경우에는 지루함을 덜 느끼게 해줄 것입니다.
새 MetaTrader 와 MQL5를 소개해드립니다 새 MetaTrader 와 MQL5를 소개해드립니다
본 문서는 MetaTrader5의 간략 리뷰입니다. 짧은 시간 내에 시스템의 모든 세부 사항을 안내해드리기는 어렵습니다 - 테스트는 2009.09.09에 시작되었습니다. 이는 상징적인 일자로, 전 이것이 행운의 숫자가 될거라 믿어 의심치않습니다. 제가 새 MetaTrader 5 터미널과 MQL5 베타버전을 받은지 며칠이 지났습니다. 아직 모든 기능을 사용해본 것은 아니지만, 벌써부터 감명깊네요.
빌 윌리엄스의 MFI로 트레이딩 시스템을 설계하는 방법 알아보기 빌 윌리엄스의 MFI로 트레이딩 시스템을 설계하는 방법 알아보기
이번 글은 인기 있는 보조지표를 기반으로 트레이딩 시스템을 설계하는 방법을 알아보는 시리즈의 새로운 글입니다. 이번에는 빌 윌리엄스의 시장 촉진 지수(BW MFI)를 다뤄보겠습니다.