English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
preview
하나의 차트에 여러 개의 지표 넣기(파트 02): 첫 번째 실험

하나의 차트에 여러 개의 지표 넣기(파트 02): 첫 번째 실험

MetaTrader 5 | 23 6월 2022, 11:36
187 0
Daniel Jose
Daniel Jose

소개

이전 기사"하나의 차트에 여러 개의 지표 넣기"에서 너무 많은 서로 다른 세부적인 정보로 화면을 채우지 않고 하나의 차트에서 여러 개의 지표를 사용하는 방법에 대한 개념과 기본 사항을 제시했습니다. 이 기사의 유일한 목적은 시스템 자체를 제시하고 이를 위해 데이터베이스를 생성하는 방법과 그러한 데이터베이스를 활용하는 방법을 보여주는 것이었습니다. 이전에는 시스템 코드를 제공하지 않았습니다. 이 글에서는 코드를 구현하는 방법에 대해 알아 보고 이후의 기사에서는 시스템의 기능을 확장하여 시스템 기능을 더욱 다재다능하고 완전하게 만들 것입니다.


계획

아이디어를 이해하기 쉽게 하고 시스템을 확장할 수 있도록 하기 위해 메인 코드는 OOP(객체 지향 프로그래밍)을 사용하면서 두 개의 개별 파일로 분할되었습니다. 이 모든 것이 시스템이 지속 가능하고 안전하며 안정적인 방식으로 개발될 수 있도록 합니다.

첫 번째 단계는 이 창에 지표를 생성해 보겠습니다.

다른 파일 형식이 아닌 지표를 사용하는 이유는 무엇일까요? 그 이유는 지표를 사용하면 하위 창을 생성하기 위한 추가적인 코드를 구현할 필요가 없기 때문입니다. 대신 지표에 이를 수행하도록 지시할 수 있으므로 시간이 절약되고 시스템 개발 속도가 빨라집니다. 지표 헤더는 다음과 같습니다:

#property indicator_plots 0
#property indicator_separate_window

이 두 줄만 있으면 심볼 차트에 하위 창을 만들 수 있습니다(작동 방식을 모르는 사람들은 아래 표 참조).

코드 설명
indicador_plots 0 이 줄은 컴파일러에게 데이터 유형을 추적하지 않을 것임을 알려줍니다. 컴파일러가 경고 메시지를 표시하는 것을 방지합니다.
Indicator_separate_window 이 줄은 하위 창을 만드는 데 필요한 논리를 추가하도록 컴파일러에 지시합니다.

이것은 쉬워야 합니다. 프로그래밍에 익숙하지 않은 사람들에게는 소스 코드의 일부 항목이 이상하게 보일 수 있지만 이들 항목은 전체 프로그래밍 커뮤니티에서 허용하는 널리 사용되는 프로토콜을 따르는 것입니다. MetaTrader 5는 C++와 매우 유사한 MQL5 언어를 사용하기 때문에 C++에서 사용하는 것과 동일한 프로그래밍 방식을 사용할 수 있습니다. 따라서 일이 훨씬 쉬워집니다. 이러한 사실을 염두 해 둔다면 다음과 같이 C 언어 지시문을 사용할 수 있습니다.

 #include <Auxiliary\C_TemplateChart.mqh>

이 지시문은 특정 위치에 존재하는 헤더 파일을 포함하도록 컴파일러에 지시합니다. 전체 경로는Include \ Auxiliary \ C_TemplateChart.mqh와 같아야 하지 않을까요? 예, 전체 경로는 다음과 같습니다. 그러나 MQL5는 헤더 파일이 'includes' 디렉토리에 있어야 한다고 가정합니다. 그러므로 첫 번째 부분을 생략할 수 있습니다. 경로가 꺾쇠 괄호로 묶이면 절대 경로입니다. 따옴표로 묶인 경우 경로는 상대 경로입니다(예:<Auxiliary \ C_TemplateChart. mqh>"Auxiliary \ C_TemplateChart.mqh"와 다릅니다.

코드를 계속 진행하면 다음과 같은 행이 표시됩니다:

input string user01 = "" ;       //Used indicators
input string user02 = "" ;       //Assets to follow

여기에 문자열 값을 입력할 수 있습니다. 지표를 열 때 사용하려는 명령이 무엇인지 명확하게 알고 있다면 여기에서 기본값을 지정할 수 있습니다. 예를 들어, 항상 선 너비가 3인 RSI와 선 너비가 2인 MACD를 사용하려고 할 경우. 디폴트를 다음과 같이 지정합니다.

input string user01 = "RSI:3;MACD:2" ;  //Used indicators
input string user02 = "" ;              //Assets to follow

명령은 나중에 변경할 수 있지만 기본값을 사용하면 명령을 사용할 것으로 이미 구성되어 있으므로 지표를 더 쉽게 열 수 있습니다. 다음 줄은 모든 "무거운" 코드가 포함된 객체 클래스에 액세스할 수 있는 별칭을 만들어 우리가 공개 함수에 액세스할 수 있도록 합니다.

C_TemplateChart SubWin;

사용자 지정 지표 파일의 코드는 거의 준비되었으며 모든 것이 작동하려면 3줄만 추가하면 됩니다. 물론 객체 클래스에는 오류가 포함되어 있지 않지만 이 클래스에서는 내부에서 이를 볼 수 있습니다. 따라서 지표 파일을 완료하려면 녹색으로 강조 표시된 다음의 행을 추가하십시오.

 //+------------------------------------------------------------------+
int OnInit ()
{
         SubWin.AddThese(C_TemplateChart::INDICATOR, user01);
         SubWin.AddThese(C_TemplateChart::SYMBOL, user02);

         return INIT_SUCCEEDED ;
}
//+------------------------------------------------------------------+

//...... other lines are of no interest to us ......

//+------------------------------------------------------------------+
void OnChartEvent ( const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
{
         if (id == CHARTEVENT_CHART_CHANGE ) SubWin.Resize();
}
//+------------------------------------------------------------------+

이것이 바로 사용자 지정 지표의 코드가 될 것입니다. 이제 객체 클래스가 포함된 파일의 블랙박스를 자세히 살펴보겠습니다. 이제부터는 두 가지 기능에 집중해야 하지만 먼저 객체 클래스에 있는 함수를 살펴보고 각각의 용도를 살펴보겠습니다.

함수 설명
SetBase 지표 데이터를 표시하는 데 필요한 개체를 생성
decode 전달된 명령을 디코딩합니다.
AddTemplate 데이터 유형에 따라 적절하게 조정
C_Template 기본 클래스 생성자
~ C_Template 클래스 소멸자
Resize 하위 창 크기 변경
AddThese 내부 객체에 접근하고 생성하는 함수

이게 전부입니다. 보시다시피 사용자 지정 지표에서 RESIZE 및 ADDTHESE 기능을 사용합니다. 이것들은 현재 유일한 공용 함수입니다. 즉 수정되지 않도록 다른 모든 것이 객체 내부에 숨겨져 있으므로 걱정할 필요가 거의 없습니다. 이것은 완성된 코드에 높은 신뢰성을 제공합니다. 다음과 같은 정의로 시작하는 코드를 계속 진행해 보겠습니다:

 #define def_MaxTemplates         6

이 줄은 객체 클래스에 매우 중요합니다. 왜냐하면 생성할 수 있는 최대 포인터 수를 정의하기 때문입니다. 더 많거나 더 적게 추가하려면 이 숫자를 변경하기만 하면 됩니다. 이 간단한 솔루션으로 동적 메모리 할당이 가능하고 지표의 수를 제한합니다. 아마도 이 부분이 여러분이 변경하고 싶은 유일한 부분이겠지만 6이 대부분의 사람들과 사용하는 모니터에 적합한 숫자라고 생각합니다.

다음 줄은 프로그램에서 데이터를 더 쉽게 관리할 수 있도록 하는 열거입니다.

 enum eTypeChart {INDICATOR, SYMBOL};

이 줄이 우리의 클래스 안에 있다는 사실은 이 클래스가 다른 클래스에서도 동일한 이름을 가질 수 있다는 것을 말합니다. 그러나 여기에 사용한 클래스 안에 지정된 데이터는 이 객체 클래스에만 속합니다. 따라서 이 열거형에 올바르게 액세스하려면 사용자 지정 지표 파일의 OnInit 함수에 제공된 양식을 사용하십시오. 클래스 이름이 생략되면 구문 오류로 간주되어 코드가 컴파일되지 않습니다. 다음 줄은 예약어입니다.

 private :

여기로 오는 모든 것이 이 객체 클래스에 대해 비공개가 되며 클래스 외부에서 볼 수 없음을 의미합니다. 즉, 클래스 안에 있지 않으면 더 이상 액세스할 수 없습니다. 이는 코드의 보안을 향상시켜서 클래스별 개인 데이터를 다른 곳에서 액세스할 수 없도록 합니다. 이후의 라인에서는 우리의 클래스의 첫 번째에 있는 실제의 함수에 도달하기 전까지의 내부 및 개인 변수를 선언합니다.

 void SetBase( const string szSymbol, int scale)
{
#define macro_SetInteger(A, B) ObjectSetInteger (m_Id, m_szObjName[m_Counter], A, B)

...

         ObjectCreate (m_Id, m_szObjName[m_Counter], OBJ_CHART , m_IdSubWin, 0 , 0 );
         ObjectSetString (m_Id, m_szObjName[m_Counter], OBJPROP_SYMBOL , szSymbol);
        macro_SetInteger( OBJPROP_CHART_SCALE , scale);
...
        macro_SetInteger( OBJPROP_PERIOD , _Period );
        m_handle = ObjectGetInteger (m_Id, m_szObjName[m_Counter], OBJPROP_CHART_ID );
        m_Counter++;
#undef macro_SetInteger
};


이 SetBase 코드의 각 부분을 더 자세히 살펴보겠습니다. 처음에는 매크로를 선언하는 것으로 시작합니다. 매크로는 컴파일러에게 매크로의 이름에 대응하는 단순화된 코드를 해석하는 방법을 알려줍니다. 즉, 무언가를 여러 번 반복해야 하는 경우 C 언어의 이 기능을 사용하여 더 간단하게 작업할 수 있습니다. 무언가를 변경해야 하는 경우 매크로에서 변경하면 되는 것입니다. 이렇게 하면 작업 속도가 크게 빨라지고 한 부분이나 다른 인수만 수정되는 경우와 같은 코드의 오류 가능성이 줄어듭니다.

이렇게 해서 우리는 CHART 유형의 객체를 생성합니다. 이것은 이상하게 보일 수 있습니다. 변경된 것에 변경될 수 있는 것을 사용하는 이유는 무엇입니까? 그건 맞습니다. 다음 단계는 사용할 자산을 선언하는 것입니다. 여기서 첫 번째 요점: 차트 저장 시 자산이 없으면 객체는 현재 사용 중인 자산에 연결됩니다. 만약 특정 자산의 차트인 경우 정확히 이 자산이 나중에 사용됩니다. 중요 세부 사항: 다른 자산을 표시하고 일반 설정을 사용할 수 있지만 다음 기사에서 자세히 설명하겠습니다. 우리는 현재 구현 불가능한 것을 할 수 있도록 이 코드에서 몇 가지 개선 사항을 구현할 것이기 때문입니다. 다음으로 OBJPROP_CHART_SCALE 속성에 표시되는 정보와 관련한 밀도의 수준이 있습니다. 우리는 0에서 5까지의 값을 사용할 것입니다. 이 범위를 벗어나는 값을 사용할 수 있지만 유지하는 것이 좋습니다.

다음으로 주의해야 할 사항은 OBJPROP_PERIOD 속성입니다. 우리는 현재의 차트 기간을 사용하고 있으며 기간을 변경하면 이 기간도 변경됩니다. 앞으로는 잠글 수 있도록 몇 가지 수정을 할 것입니다. 시도하고 싶다면 MetaTrader 5에서 정의한 기간(예: PERIOD_M10)을 사용할 수 있습니다. 이 기간은 고정된 10분 기간의 데이터의 표시를 의미합니다. 그러나 이것은 나중에 개선될 것입니다. 그 후, 우리는 sub0indicator의 수를 1씩 증가시키고 매크로를 없에 버립니다. 즉, 매크로에는 더 이상 나타낼 것이 없으며 다른 곳에서 사용하려면 재정의해야 합니다. 제가 뭔가를 잊지 않았을까요! 예 라인입니다. 아마도 이 코드에서 가장 중요한 부분일 것입니다.

m_handle = ObjectGetInteger (m_Id, m_szObjName[m_Counter], OBJPROP_CHART_ID );

이 라인은 포인터로 간주될 수 있는 것을 캡처하지만 실제로는 포인터 자체는 아닙니다. 그러나 우리가 생성한 OBJ_CHART 객체에 대해 몇 가지 추가적인 조작을 수행할 수 있게 합니다. 객체 내부의 일부 설정을 적용하려면 이 값이 필요합니다. 이것들은 우리가 이전에 만든 설정 파일에 있습니다. 코드를 계속 진행하면 아래에서 볼 수 있는 다음의 함수를 보게 됩니다.

 void AddTemplate( const eTypeChart type, const string szTemplate, int scale)
{
	if (m_Counter >= def_MaxTemplates) return ;
	if (type == SYMBOL) SymbolSelect (szTemplate, true );
	SetBase((type == INDICATOR ? _Symbol : szTemplate), scale);
	ChartApplyTemplate (m_handle, szTemplate + ".tpl" );
	ChartRedraw (m_handle);
}

먼저 새로운 지표를 추가할 수 있는지 여부를 확인합니다. 가능하면 SYMBOL인지를 확인하고 맞다면 SYMBOL은Market Watch창에 있어야 합니다. 이는 함수가 기능하여 구현되는 것입니다. 이를 기반으로 정보를 수신할 객체를 만듭니다. 실행 시 템플릿이 OBJ_CHART에 적용되고 그 때 마법이 발생합니다. 객체를 다시 호출하지만 이제 OBJ_CHART를 정의하는 데 사용된 설정 파일에 포함된 정의에 따라 데이터가 포함됩니다. 간단하고 아름답고 이해가 갑니다.

이 두 가지 함수를 사용하여 많은 작업을 수행할 수 있습니다. 그러나 우리는 적어도 하나의 함수가 더 필요합니다 - 전체 코드는 아래와 같습니다.

 void Resize( void )
{
         int x0 = 0 , x1 = ( int )( ChartGetInteger (m_Id, CHART_WIDTH_IN_PIXELS , m_IdSubWin) / (m_Counter > 0 ? m_Counter : 1 ));
         for ( char c0 = 0 ; c0 < m_Counter; c0++, x0 += x1)
        {
                 ObjectSetInteger (m_Id, m_szObjName[c0], OBJPROP_XDISTANCE , x0);
                 ObjectSetInteger (m_Id, m_szObjName[c0], OBJPROP_XSIZE , x1);
                 ObjectSetInteger (m_Id, m_szObjName[c0], OBJPROP_YSIZE , ChartGetInteger (m_Id, CHART_HEIGHT_IN_PIXELS , m_IdSubWin));
        }
         ChartRedraw ();
}

위의 함수가 하는 일은 모든 것을 제자리에 놓고 데이터를 항상 하위 창 안에 있도록 하고 여기에 더 이상 추가할 것이 없다는 것입니다. 따라서 모든 것이 완벽하게 작동하는 데 필요한 모든 코드를 완성했습니다. 다른 함수는 어떨까요?! 걱정하지 마십시오. 나머지 루틴은 전혀 필요하지 않으며 단순히 명령줄을 해석하는 것을 도우는 것입니다. 어쨌든 앞으로 이 코드를 수정하려는 사람에게 중요한 것들을 살펴보겠습니다. 객체 클래스 코드에 나타나는 예약어입니다.

 public   :

이 단어는 이 순간부터 모든 데이터와 함수들이 객체 클래스의 일부가 아니더라도 코드의 다른 부분에서 액세스하고 볼 수 있음을 의미합니다. 그래서 여기서 우리는 실제로 변경되거나 다른 객체에 의해 액세스될 수 있는 것들을 선언합니다. 사실 좋은 객체 지향 코드는 객체의 데이터에 대한 직접적인 접근을 절대 허용하지 않는 것입니다. 잘 설계된 코드에서는 메서드에만 액세스할 수 있습니다. 그 이유는 간단합니다 - 보안. 외부의 코드가 클래스 내의 데이터를 수정하도록 허용하게 되면 객체가 기대하는 것과 데이터가 일치하지 않을 위험이 있으며 이는 모든 것이 이상이 없는것처럼 보임에도 불구하고 불일치 하거나 결함이 발생하고 이를 해결하려고 할 때 많은 문제와 골칫거리를 유발할 수 있습니다. 그래서 C++로 프로그래밍을 해 온 사람에게 해주고 싶은 몇 가지 조언이 있습니다. 여러분이 만든 클래스의 데이터를 외부의 객체가 수정하거나 직접 액세스하도록 허용하지 마십시오. 데이터에 액세스할 수 있도록 함수 또는 루틴을 제공하되 데이터에 직접 액세스하는 것을 절대 허용하지 말고 함수 나 루틴이 생성한 클래스에서 예상한 대로 데이터를 지원하는지 확인하십시오. 이를 염두에 두고 튜토리얼의 마지막 두 함수로 넘어가 보겠습니다. 하나는 공개(AddThese)이고 다른 하나는 비공개(Decode)입니다. 아래에서 전체 내용을 볼 수 있습니다.

void Decode( string &szArg, int &iScale)
{
#define def_ScaleDefault 4
         StringToUpper (szArg);
        iScale = def_ScaleDefault;
         for ( int c0 = 0 , c1 = 0 , max = StringLen (szArg); c0 < max; c0++) switch (szArg[c0])
        {
                 case ':' :
                         for (; (c0 < max) && ((szArg[c0] < '0' ) || (szArg[c0] > '9' )); c0++);
                        iScale = ( int )(szArg[c0] - '0' );
                        iScale = ((iScale > 5 ) || (iScale < 0 ) ? def_ScaleDefault : iScale);
                        szArg = StringSubstr (szArg, 0 , c1 + 1 );
                         return ;
                 case ' ' :
                         break ;
                 default :
                        c1 = c0;
                         break ;
        }
#undef def_ScaleDefault
}
//+------------------------------------------------------------------+
// ... Codes not related to this part...
//+------------------------------------------------------------------+
void AddThese( const eTypeChart type, string szArg)
{
         string szLoc;
         int i0;
         StringToUpper (szArg);
         StringAdd (szArg, ";" );
         for ( int c0 = 0 , c1 = 0 , c2 = 0 , max = StringLen (szArg); c0 < max; c0++) switch (szArg[c0])
        {
                 case ';' :
                         if (c1 != c2)
                        {
                                szLoc = StringSubstr (szArg, c1, c2 - c1 + 1 );
                                Decode(szLoc, i0);
                                AddTemplate(type, szLoc, i0);
                        }
                        c1 = c2 = (c0 + 1 );
                         break ;
                 case ' ' :
                        c1 = (c1 >= c2 ? c0 + 1 : c1);
                         break ;
                 default :
                        c2 = c0;
                         break ;
        }
}

이 두 함수는 위에서 설명한 것과 정확히 일치합니다: 일치하지 않는 데이터가 클래스의 내부 데이터의 일부가 되는 것을 방지하여 객체 클래스 내에서의 데이터의 무결성을 확립합니다. 이들 함수는 명령줄을 수신하고 미리 정의된 구문에 따라 디코딩합니다. 그러나 수신된 명령에 오류가 있다고 알리지는 않습니다. 그러한 목적으로 만들어진 것이 아니기 때문입니다. 함수의 목적은 일관성 없는 데이터가 객체에 들어가지 않도록 하고 감지하거나 수정하기 어려운 부작용을 일으키지 않도록 하는 것입니다.

최종 결과는 다음과 같습니다.



결론

이 코드가 당신에게 영감을 주기를 바랍니다! 저는 프로그래밍에 관심을 갖게 된 계기가 아름답고 흥미진진해서였습니다. 때로는 특별한 결과를 얻을 수 있기를 원할때 동시에 많은 골치거리를 주기도 합니다. 그러나 대부분의 경우 프로그래밍은 가치가 있습니다. 다음 기사에서는 이 모든 것을 더욱 흥미롭게 만드는 방법에 대해 알아 보겠습니다. 이 기사의 첨부 파일에는 이 기사와 이전 기사에서 다룬 즉시 사용 가능한 지표의 전체 코드가 포함되어 있습니다.


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

하나의 차트에 여러개의 지표 넣기(파트 03): 사용자 정의 개발 하나의 차트에 여러개의 지표 넣기(파트 03): 사용자 정의 개발
오늘은 지표 시스템의 기능을 업데이트할 것입니다. "하나의 차트에 여러 개의 지표 넣기"의 이전 기사에서 우리는 차트 하위 창에서 두개 이상의 지표를 사용할 수 있도록 하게 하는 기본 코드 살펴 보았습니다. 그러나 살펴본 내용은 훨씬 더 큰 시스템을 시작하기 위한 기반에 불과합니다.
하나의 차트에 여러 개의 지표 넣기(파트 01): 개념 이해 하나의 차트에 여러 개의 지표 넣기(파트 01): 개념 이해
오늘 우리는 하나의 차트에서 여러개의 지표가 동시에 실행되면서 지표별로 각각의 영역을 차지하지 않는 차트에 지표를 추가하는 방법에 대해 알아 봅니다. 많은 트레이더들은 한 번에 여러개의 지표(예: RSI, STOCATIC, MACD, ADX 및 기타)를 모니터링하거 때로는 다른 자산을 인덱스로 만들어 모니터링할 때 거래에 대해 좀 더 자신감을 가지게 되기도 합니다.
Envelopes로 트레이딩 시스템을 설계하는 방법을 배우보세요 Envelopes로 트레이딩 시스템을 설계하는 방법을 배우보세요
이 글에서는 밴드 거래 방법 중 하나를 알려 드리겠습니다. 이번에는 Envelopes를 살펴보고 Envelopes를 기반으로 몇 가지 전략을 만드는 것이 얼마나 쉬운지 알아보겠습니다.
Bollinger Bands 기반의 트레이딩 시스템 설계 방법에 대해 알아보기 Bollinger Bands 기반의 트레이딩 시스템 설계 방법에 대해 알아보기
이 기사에서는 트레이딩 세계에서 가장 인기 있는 지표 중 하나인 볼린저 밴드에 대해 알아보겠습니다. 우리는 기술적 분석을 고려하면서 볼린저 밴드 지표를 기반으로 하는 알고리즘 트레이딩 시스템을 설계하는 방법에 대해 알아 볼 것입니다.