
MQL5를 사용하여 캔들 스틱 패턴을 감지하는 방법
소개
캔들 스틱은 패턴을 기반으로 잠재적인 움직임을 찾을 수 있기 때문에 올바르게 사용할 경우 매우 유용한 기술적 도구입니다. 캔들은 차트에서 특정 패턴을 형성할 수 있으며 이러한 패턴은 단일 캔들 패턴과 혼합 캔들 패턴(두 개 이상의 캔들)의 두 가지 유형으로 나뉠 수 있습니다. 이 기사에서는 MQL5를 사용하여 MetaTrader 5 트레이딩 터미널에서 이러한 패턴 중 일부를 자동으로 감지하는 방법에 대해 알아볼 것입니다. 우리는 다음의 항목들을 통해 이를 다룰 것입니다:
중요한 신호를 얻으려면 이러한 패턴을 다른 기술적 도구와 함께 사용해야 합니다. 따라서 트레이딩을 쉽게 하고 좋은 결과를 얻으려면 우리는 MQL5가 이들 패턴을 감지하는 주요 아이디어를 이해해야 합니다.
단일 캔들 패턴
여기서는 차트에 나타나는 인기 있는 단일 캔들 패턴의 두 가지 예를 살펴보겠습니다. 도지 패턴과 해머 패턴은 어느 시간대에나 볼 수 있으며 가격 움직임에 따라 형태를 이루게 되면 더 중요해 집니다.
도지 패턴:
캔들 패턴 중 매우 인기가 있으며 시가와 종가가 거의 동일한 캔들입니다. 캔들의 몸통이 매우 작거나 차트에서 상하 몸통이 같은 가격에 대한 선을 볼 수 있거나 이러한 그림자가 없는 캔들입니다. 다음 그림은 이 캔들에 대한 그림입니다:
이 도지 캔들은 매수자와 매도자 사이에 균형이 있으며 도지 캔들이 나타나는 기간 동안 가격을 높이거나 낮추기 위해 시장을 컨트롤하는 사람이 없음을 나타냅니다. 조정 전이나 추세의 끝에서 차트의 적절한 위치에서 나타나는 경우 시장의 반전 또는 조정 신호일 수 있으며 더 큰 시간대에 나타나면 더 중요한 의미를 갖습니다. 이 캔들에는 다양한 종류와 형태가 있으며 잠자리형과 긴다리형처럼 누구나 거래에 유리하게 사용할 수 있는 것들이 있습니다.
우리가 해야 할 일은 컴퓨터가 마지막 캔들의 가격과 시간을 정의하여 도지 패턴을 감지하도록 알려주는 것이며 매 틱마다 프로그램이 이 정의된 시간에 이 값을 확인하고 비교하여 모든 사람의 포지션을 결정해야 합니다. 시가가 종가와 같으면 프로그램은 이것이 도지 캔들 패턴이라는 신호를 반환해야 합니다.
이제 우리는 이 패턴을 감지할 수 있는 프로그램을 만들어야 합니다. 이를 위한 단계는 다음과 같습니다:
우리는 이 도지에 대한 함수(getDoji)를 생성하고 이 패턴을 검색하는 모든 틱을 확인하기 위해 OnTick()에서 함수를 호출할 것입니다.
void OnTick() { getDoji(); }
(getDoji) 함수를 정수 변수로 생성하여 만듭니다.
int getDoji()
마지막 캔들의 시간, 시가, 고가, 저가, 종가를 정의하여 이 함수를 정의합니다.
캔들이 생성된 시간을 반환하는 iTime 함수, 캔들의 시가를 반환하는 iOpen, 고가를 반환하는 iHigh, 저가를 반환하는 iLow, 종가를 반환하는 iClose를 사용하는데, 매개변수는 모두 동일합니다:
- 심볼: 심볼 이름을 정의하기 위해 현재 심볼에 (_Symbol)을 사용합니다.
- TIMEFRAME: 차트 주기 또는 기간을 정의하기 위해 현재 기간 (PERIOD_CURRENT)를 사용합니다.
- shift: 반환된 값의 인덱스를 정의하기 위해 (1)을 사용하여 마지막 캔들을 정의합니다.
datetime time=iTime(_Symbol,PERIOD_CURRENT,1); double open=iOpen(_Symbol,PERIOD_CURRENT,1); double high=iHigh(_Symbol,PERIOD_CURRENT,1); double low=iLow(_Symbol,PERIOD_CURRENT,1); double close=iClose(_Symbol,PERIOD_CURRENT,1);
if 문을 사용하여 우리가 감지해야 하는 도지의 조건을 설정합니다.
if(open==close)
이 조건이 참이면 프로그램은 시간, 가격, 화살표 코드, 색상 및 필요한 텍스트의 매개 변수를 사용하여 생성할 createObj 함수를 기반으로 객체를 생성해야 합니다. 그런 다음 1을 반환하여 함수를 종료합니다.
if(open==close) { createObj(time,low,217, clrBlack,"Doji"); { return 1; } }
우리는 0을 반환하여 getDoji의 함수를 종료할 것입니다.
return 0;
void 함수를 사용하여 시간, 가격, arrowcode, clr 및 txt 매개 변수를 사용하여 생성(createObj) 함수 만들기
void createObj(datetime time, double price, int arrawCode, color clr, string txt)
(" ") 값에 할당된 (objName)의 문자열 변수 만들기
string objName=" ";
전달된 매개변수의 문자열을 형성하고 형성된 문자열의 크기를 반환하는 (StringConcatenate) 함수를 사용하여 문자열을 결합하여 (objName) 변수에 할당합니다. 매개변수는 다음과 같습니다:
- string_var: 연결 후 형성될 문자열을 정의하기 위해 (objName)을 사용합니다.
- argument1: 간단한 유형의 매개변수를 정의하기 위해 "Signal at " 텍스트를 사용합니다.
- argument2: 감지된 캔들의 시간을 정의하기 위해 미리 정해진 변수의 시간을 사용합니다.
- argument3: "at"의 텍스트를 설정합니다.
- argument4: DoubleToString을 사용하여 더블 유형을 문자열 유형으로 변환하여 반올림된 가격의 텍스트를 설정합니다.
- argument5: "("의 텍스트를 설정합니다.
- argument6: 우리에게 필요한 미리 정해진 정수 변수(arrowcode)에 값을 할당합니다. 이 코드는 mql5 참조에서 Wingdings를 검색하면 찾을 수 있습니다.
- argument7: ")"의 텍스트를 설정합니다.
StringConcatenate(objName, "Signal at ",time, " at ",DoubleToString(price,_Digits)," (",arrawCode,")");
if 문과 (ObjectCreate) 함수를 표현식으로 사용하여 평가할 조건을 설정하고 (ObjectCreate) 함수는 미리 정의된 이름(objName)과 해당 매개 변수를 사용하여 객체를 생성합니다:
- chart_id: 차트를 식별하기 위해 현재 차트에는 0을 사용합니다.
- name: 객체 이름을 정의하기 위해 미리 정의된 이름(objName)을 사용합니다.
- type: 객체 유형을 정의하기 위해 (OBJ_ARROW)를 사용합니다.
- nwin: 차트 하위 창의 수를 정의하기 위해 기본 차트 창에는 (0)을 사용합니다.
- time1: 앵커의 시간을 정의하기 위해 미리 정의된 (time) 변수를 사용합니다.
- price1: 앵커의 가격을 정의하기 위해 미리 정의된 (price) 변수를 사용합니다.
if(ObjectCreate(0,objName,OBJ_ARROW,0,time,price))
객체를 생성하여 이 조건이 참이면 객체의 속성 값을 설정하는 (ObjectSetInteger) 함수를 사용하여 화살표 코드와 색상을 결정하고 객체 속성의 모양을 설정해야 합니다. 매개변수는 다음과 같습니다:
- chart_id: 차트를 식별하기 위해 현재 차트에는 0을 사용합니다.
- name: 객체 이름을 정의하기 위해 (objName)을 사용합니다.
- prop_id: 객체의 속성을 정의하기 위한 것으로 ENUM_OBJECT_PROPERTY_INTEGER 중 하나인 화살표 코드는 (OBJPROP_ARROWCODE), 색상은 (OBJPROP_COLOR)을 사용할 것입니다.
- prop_value: 속성 값을 정의하기 위해 화살표 코드는 (arrawCode)를 색상에는 미리 정의된 변수(clr)를 사용합니다.
ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrawCode); ObjectSetInteger(0,objName,OBJPROP_COLOR,clr);
그런 다음 미리 정의된 (objName) 및 (txt) 변수를 할당하여 (candleName)에 대한 문자열 변수를 생성하여 캔들의 정의로 우리에게 필요한 텍스트를 정의해야 합니다.
string candleName=objName+txt;
if 문을 사용하여 텍스트 객체를 생성하고 편집합니다.(ObjectCreate) 함수를 표현식으로 사용하고 연산자는 객체 속성의 문자열 값을 설정하는 (ObjectSetString)과 텍스트 객체의 색상을 설정하는 (ObjectSetInteger)를 사용합니다.
ObjectSetString(0,candleName,OBJPROP_TEXT," "+txt); ObjectSetInteger(0,candleName,OBJPROP_COLOR,clr);
이제 우리는 이 Expert Advisor의 전체 코드를 다음과 같이 확인할 수 있습니다:
//+------------------------------------------------------------------+ //| Doji pattern detector.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ void OnTick() { getDoji(); } int getDoji() { datetime time=iTime(_Symbol,PERIOD_CURRENT,1); double open=iOpen(_Symbol,PERIOD_CURRENT,1); double high=iHigh(_Symbol,PERIOD_CURRENT,1); double low=iLow(_Symbol,PERIOD_CURRENT,1); double close=iClose(_Symbol,PERIOD_CURRENT,1); //Doji if(open==close) { createObj(time,low,217, clrBlack,"Doji"); { return 1; } } return 0; } void createObj(datetime time, double price, int arrawCode, color clr, string txt) { string objName=" "; StringConcatenate(objName, "Signal at ",time, " at ",DoubleToString(price,_Digits)," (",arrawCode,")"); if(ObjectCreate(0,objName,OBJ_ARROW,0,time,price)) { ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrawCode); ObjectSetInteger(0,objName,OBJPROP_COLOR,clr); } string candleName=objName+txt; if(ObjectCreate(0,candleName,OBJ_TEXT,0,time,price)) { ObjectSetString(0,candleName,OBJPROP_TEXT," "+txt); ObjectSetInteger(0,candleName,OBJPROP_COLOR,clr); } }
이 코드를 오류 없이 컴파일하고 내비게이터 창에서 찾을 수 있습니다. 드래그하여 실행하면 도지 패턴을 감지하는 신호를 얻을 수 있습니다. 다음은 테스트의 예입니다:
이전 차트에서 볼 수 있듯이 캔들 아래에 검은색 화살표 객체와 캔들 패턴을 정의하는 도지 텍스트가 있습니다.
해머 패턴:
해머 패턴은 차트에서 여러 차트 주기에 걸쳐 볼 수 있는 매우 인기 있는 캔들 패턴입니다. 이 이름은 그림자가 길고 몸통이 작은 모양에서 유래한 것입니다. 작은 몸통의 위치에 따라 해머와 거꾸로 해머 두 가지 유형의 패턴이 있습니다. 아래쪽 그림자가 길고 캔들 몸통이 위에 있으면 해머형이며 시가와 종가를 기준으로 강세 또는 약세 캔들일 수 있습니다. 다음 그림은 이 해머 패턴의 예시입니다:
- 강세 해머
매도세가 가격을 낮추려고 했지만 매수세가 시장을 장악하고 시초가보다 높게 마감했음을 나타내며 이는 매수세의 강세를 의미합니다.
- 약세 해머
이는 매도세가 가격을 낮추려고 했지만 매수세가 바 생성 시점에 청산한 것으로 보이며 이는 매수세가 아직 이 게임에 참여 중임을 의미합니다.
캔들 위쪽 그림자가 길고 몸통이 아래에 있는 경우 역해머 패턴이며 시가와 종가의 위치에 따라 강세 또는 약세일 수 있습니다. 다음 그림은 이 역해머의 예시입니다.
- 강세 역해머
이는 매수세가 가격을 더 높이려고 했지만 매도세가 시가와 저점 부근에서 캔들을 마감한 것으로 보이며 이는 매수세의 강세에도 불구하고 매도세가 여전히 이 게임에 참여하고 있음을 의미합니다.
- 약세 역 해머
매수세가 가격을 낮추려고 했지만 매도세가 시장을 지배하여 시가보다 낮게 마감되었음을 나타내며 이는 매도세의 강세를 의미합니다.
이 패턴은 모든 캔들 패턴과 마찬가지로 다른 기술적 도구와 결합하면 더 의미 있고 중요합니다.
이제 우리는 이런 패턴을 감지하는 데 사용할 수 있는 프로그램을 만들어야 합니다. 이 프로그램이 캔들 가격, 시간, 캔들 크기를 캔들의 몸통과 그림자와 비교하도록 하고 이들을 매 틱마다 지속적으로 확인하고 비교하여 위치를 결정하도록 해야 합니다. 프로그램이 해머 또는 역해머(상승 또는 하락) 중 하나를 감지하면 차트에 캔들 색상(상승 또는 하락)을 기준으로 캔들 아래 또는 위에 해당 유형 이름과 녹색 또는 빨간색의 화살표로 객체를 반환해야 합니다.
다음은 이러한 유형의 프로그램을 만드는 전체 코드입니다:
//+------------------------------------------------------------------+ //| Hammer pattern detector.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ void OnTick() { getHammer(0.07,0.7); } int getHammer(double smallShadowRatio, double longShadowRatio) { datetime time=iTime(_Symbol,PERIOD_CURRENT,1); double open=iOpen(_Symbol,PERIOD_CURRENT,1); double high=iHigh(_Symbol,PERIOD_CURRENT,1); double low=iLow(_Symbol,PERIOD_CURRENT,1); double close=iClose(_Symbol,PERIOD_CURRENT,1); double candleSize=high-low; if(open<close) { if(high-close < candleSize*smallShadowRatio) { if(open-low>candleSize*longShadowRatio) createObj(time,low,217, clrGreen,"Hammer"); { return 1; } } } if(open>close) { if(high-open<candleSize*smallShadowRatio) { if(close-low>candleSize*longShadowRatio) createObj(time,high,218,clrRed,"Hammer"); { return 1; } } } if(open<close) { if(open-low < candleSize*smallShadowRatio) { if(high-close>candleSize*longShadowRatio) createObj(time,low,217, clrGreen,"Inverted Hammer"); { return -1; } } } if(open>close) { if(close-low < candleSize*smallShadowRatio) { if(high-open>candleSize*longShadowRatio) createObj(time,high,218, clrRed,"Inverted Hammer"); { return -1; } } } return 0; } void createObj(datetime time, double price, int arrawCode, color clr, string txt) { string objName=" "; StringConcatenate(objName, "Signal@",time, "at",DoubleToString(price,_Digits),"(",arrawCode,")"); if(ObjectCreate(0,objName,OBJ_ARROW,0,time,price)) { ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrawCode); ObjectSetInteger(0,objName,OBJPROP_COLOR,clr); if(clr==clrGreen) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP); if(clr==clrRed) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM); } string candleName=objName+txt; if(ObjectCreate(0,candleName,OBJ_TEXT,0,time,price)) { ObjectSetString(0,candleName,OBJPROP_TEXT," "+txt); ObjectSetInteger(0,candleName,OBJPROP_COLOR,clr); } }
이 코드의 차이점은 다음과 같습니다:
smallShadowRatio과 longShadowRatio의 매개변수를 결정하여 OnTick()에서 getHammer 함수를 호출합니다.
void OnTick() { getHammer(0.07,0.7); }
두 개의 더블 변수 (smallShadowRatio) 및 (longShadowRatio)의 매개 변수를 사용하여 (getHammer) 함수 생성하기
int getHammer(double smallShadowRatio, double longShadowRatio)
비율과 비교할 (candleSize)에 대한 더블 변수 만들기
double candleSize=high-low;
해머 캔들의 상태,
강세 해머(시가<종가)의 경우 우리는 마지막 강세 캔들이 필요하며 캔들의 상단 그림자(고가-종가)가 작은 그림자 비율인 0.07보다 작고 하단 그림자(시가-저가)가 긴 그림자 비율인 0.7보다 커야 합니다. 이 해머 캔들 저점 아래 차트에 Wingdings의 코드(217)와 '해머' 텍스트 객체가 있는 녹색 화살표를 만든 다음 함수를 종료합니다.
if(open<close) { if(high-close < candleSize*smallShadowRatio) { if(open-low>candleSize*longShadowRatio) createObj(time,low,217, clrGreen,"Hammer"); { return 1; } } }
약세 해머(오픈>종가)의 경우 마지막 약세 캔들이 필요하며 캔들의 위쪽 그림자(고가-개봉)가 작은 그림자 비율인 0.07보다 작고 아래쪽 그림자(종가-저가)가 긴 그림자 비율인 0.7보다 커야 합니다. 윙딩스의 코드(218)로 빨간색 화살표를 만들고 차트에서 이 Hammer 캔들 고점 위에 "Hammer" 텍스트 객체를 표시한 다음 함수를 종료합니다.
if(open>close) { if(high-open<candleSize*smallShadowRatio) { if(close-low>candleSize*longShadowRatio) createObj(time,high,218,clrRed,"Hammer"); { return 1; } } }
상승 반전 해머(시가<종가)의 경우 마지막 상승 캔들이 필요하며 캔들의 아래쪽 그림자(시가-저점)가 작은 그림자 비율인 0.07보다 작고 위쪽 그림자(시가-고점)가 긴 그림자 비율인 0.7보다 커야 합니다. 이 해머 캔들 저점 아래 차트에 윙딩의 코드(217)와 "Inverted Hammer" 텍스트 객체가 있는 녹색 화살표를 만든 다음 함수를 종료합니다.
if(open<close) { if(open-low < candleSize*smallShadowRatio) { if(high-close>candleSize*longShadowRatio) createObj(time,low,217, clrGreen,"Inverted Hammer"); { return -1; } } }
약세 역해머(시가>종가)의 경우 마지막 하락 캔들이 필요하며 캔들의 아래쪽 그림자(종가-저가)가 작은 그림자 비율인 0.07보다 작고 위쪽 그림자(고가-종가)가 긴 그림자 비율인 0.7보다 커야 합니다. 이 캔들 고점 위의 차트에 윙딩스의 코드(218)와 "Inverted Hammer" 텍스트 객체가 있는 빨간색 화살표를 만든 다음 함수를 종료합니다.
if(open>close) { if(close-low < candleSize*smallShadowRatio) { if(high-open>candleSize*longShadowRatio) createObj(time,high,218, clrRed,"Inverted Hammer"); { return -1; } } }
'if' 연산자를 사용하여 캔들 유형에 따라 화살표 위치와 색상을 편집해 보겠습니다. 표현식은 색상이 되고 'if' 연산자(참인 경우)는 ObjectSetInteger 함수를 사용하여 화살표 위치로 사용됩니다.
녹색이 캔들 아래에 있는 경우
if(clr==clrGreen) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);
빨간색이 캔들 위에 있으면
if(clr==clrRed) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
이 코드를 오류 없이 컴파일하고 실행하면 우리는 신호를 얻을 수 있습니다. 다음은 테스트의 예입니다:
- 강세 해머:
차트에서 보시다시피 강세 해머 캔들 저점 아래에 녹색 화살표와 녹색 "Hammer" 텍스트 객체가 있는 것을 볼 수 있습니다.
- 약세 해머:
약세 해머 캔들 고점 위의 차트에 빨간색 화살표와 빨간색 "해머" 텍스트 객체가 있는 것을 볼 수 있습니다.
- 강세 반전 해머:
상승형 반전 해머 캔들 저점 아래 차트에 녹색 화살표와 녹색 "반전 해머" 텍스트 객체가 있는 것을 볼 수 있습니다.
- 약세 반전 해머:
약세 반전 해머 캔들 고점 위의 차트에 빨간색 화살표와 빨간색 "반전 해머" 텍스트 객체가 표시된 것을 볼 수 있습니다.
듀얼 캔들 패턴
이 부분에서 우리는 두 개의 캔들로 구성된 또 다른 유형의 캔들 패턴을 살펴볼 것입니다. 두 가지 인기 패턴인 Engulfing(강세 및 약세) 및 강세 피어싱 라인과 그 반대인 약세 다크 클라우드 패턴을 살펴볼 것입니다.
삼키는 패턴:
이 캔들 패턴은 차트와 기술적 분석에서 매우 인기 있는 패턴으로 두 개의 캔들 중 하나가 다른 캔들을 감싸는 형태로 작은 캔들 뒤에 큰 캔들이 있고 이 큰 캔들이 작은 캔들을 완전히 덮는 것을 의미합니다.
캔들의 색상이나 종류에 따라 이 삼키는 패턴의 유형이 있습니다:
- 강세 삼킴:
작은 약세 캔들 뒤에 큰 강세 캔들이 있고 이 강세 캔들이 작은 캔들을 감싸고 있는 것입니다. 다음 그림이 이에 대한 것입니다:
그 중요도에 따라 매수세가 시장을 지배하고 있으며 그 이후에도 가격이 계속 상승할 수 있음을 나타냅니다.
- 약세 삼킴:
그 중요도에 따라 매도세가 시장을 지배하고 있으며 그 이후에도 가격이 계속 하락할 수 있음을 나타냅니다.
이제 이 패턴을 자동으로 감지하는 프로그램은 다음과 같은 작동을 하여야 합니다. 프로그램은 마지막 캔들의 시간과 마지막 두 캔들의 가격을 정의하고 프로그램이 틱마다 이러한 값을 지속적으로 확인하고 서로 관련된 위치를 결정하여 이러한 유형의 삼킴 패턴이 있는지 여부를 확인해야 합니다. 이 삼킴 패턴이 완성되면 프로그램에서 유형(강세 또는 약세)에 따라 색깔별 화살표와 텍스트 객체인 특정 신호를 반환해야 합니다.
다음은 이 프로그램을 만드는 전체 코드입니다:
//+------------------------------------------------------------------+ //| Engulfing pattern detector.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ void OnTick() { getEngulfing(); } int getEngulfing() { datetime time=iTime(_Symbol,PERIOD_CURRENT,1); double open=iOpen(_Symbol,PERIOD_CURRENT,1); double high=iHigh(_Symbol,PERIOD_CURRENT,1); double low=iLow(_Symbol,PERIOD_CURRENT,1); double close=iClose(_Symbol,PERIOD_CURRENT,1); double open2=iOpen(_Symbol,PERIOD_CURRENT,2); double high2=iHigh(_Symbol,PERIOD_CURRENT,2); double low2=iLow(_Symbol,PERIOD_CURRENT,2); double close2=iClose(_Symbol,PERIOD_CURRENT,2); if(open<close) { if(open2>close2) { if(high>high2&&low<low2) { if(close>open2&&open<close2) { createObj(time,low,217, clrGreen,"Bullish Engulfing"); { return 1; } } } } } if(open>close) { if(open2<close2) { if(high>high2&&low<low2) { if(close<open2&&open>close2) { createObj(time,high,218, clrRed,"Bearish Engulfing"); { return -1; } } } } } return 0; } void createObj(datetime time, double price, int arrawCode, color clr, string txt) { string objName=" "; StringConcatenate(objName, "Signal@",time, "at",DoubleToString(price,_Digits),"(",arrawCode,")"); if(ObjectCreate(0,objName,OBJ_ARROW,0,time,price)) { ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrawCode); ObjectSetInteger(0,objName,OBJPROP_COLOR,clr); if(clr==clrGreen) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP); if(clr==clrRed) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM); } string candleName=objName+txt; if(ObjectCreate(0,candleName,OBJ_TEXT,0,time,price)) { ObjectSetString(0,candleName,OBJPROP_TEXT," "+txt); ObjectSetInteger(0,candleName,OBJPROP_COLOR,clr); } }
이 코드에서의 차이점:
(getEngulfing) 함수를 만들 때 마지막 캔들의 시간과 마지막 두 캔들의 가격에 대한 더블 변수를 생성할 때, time, open, high, low, close는 마지막 캔들에 대한 것이고 open2, high2, low2, close2는 마지막 캔들의 이전 캔들에 대한 것입니다.
datetime time=iTime(_Symbol,PERIOD_CURRENT,1); double open=iOpen(_Symbol,PERIOD_CURRENT,1); double high=iHigh(_Symbol,PERIOD_CURRENT,1); double low=iLow(_Symbol,PERIOD_CURRENT,1); double close=iClose(_Symbol,PERIOD_CURRENT,1); double open2=iOpen(_Symbol,PERIOD_CURRENT,2); double high2=iHigh(_Symbol,PERIOD_CURRENT,2); double low2=iLow(_Symbol,PERIOD_CURRENT,2); double close2=iClose(_Symbol,PERIOD_CURRENT,2);
이러한 유형의 캔들 패턴을 정의하는 조건
상승 삼킴형의 경우 마지막 캔들이 강세(시가<종가), 마지막 캔들 중 이전 캔들이 약세(시가2>종가2), 고가가 시가2보다 크고 동시에 저가가 시가2보다 낮고, 종가가 시가2보다 크고 동시에 시가2보다 낮은 경우입니다. 식별이 완료되면 생성된 함수(createObj)를 기반으로 다음 매개변수를 사용하여 객체를 생성합니다:
- time: 미리 정의된 변수인 마지막 캔들의 시간이 됩니다.
- price: 마지막 캔들의 저점이며 그 아래에 객체가 필요합니다.
- arrowCode: 윙딩스에서 217이 됩니다.
- clr: clrGreen이 됩니다.
- txt: "Bullish Engulfing"가 될 것입니다.
그런 다음 함수를 종료합니다.
if(open<close) { if(open2>close2) { if(high>high2&&low<low2) { if(close>open2&&open<close2) { createObj(time,low,217, clrGreen,"Bullish Engulfing"); { return 1; } } } } }
약세 삼킴의 경우 마지막 캔들이 약세(시가>종가), 마지막 캔들 중 이전 캔들이 강세(시가2<종가2), 고가가 고가2보다 크고 저가가 저가2보다 낮으며 종가가 시가2보다 낮고 동시에 시가2보다 큰 캔들입니다. 식별이 완료되면 생성된 함수(createObj)를 기반으로 다음 매개변수를 사용하여 객체를 생성합니다:
- time: 미리 정의된 변수인 마지막 캔들의 시간이 됩니다.
- price: 마지막 캔들의 고점이며, 그 위에 오브젝트가 필요합니다.
- arrowCode: 윙딩스에서 218이 됩니다.
- clr: clrRed가 됩니다.
- txt: "Bearish Engulfing"가 될 것입니다.
그런 다음 함수를 종료합니다.
if(open>close) { if(open2<close2) { if(high>high2&&low<low2) { if(close<open2&&open>close2) { createObj(time,high,218, clrRed,"Bearish Engulfing"); { return -1; } } } } }
이 코드를 오류 없이 컴파일하고 EA를 실행하면 테스트에서 다음 예시와 같은 신호를 얻을 수 있습니다:
- 강세 삼킴:
이전 차트에서 볼 수 있듯이 패턴의 마지막 캔들 저점 아래에 녹색 화살표와 상승 삼킴형 텍스트가 있습니다.
- 약세 삼킴:
이전 차트에서 볼 수 있듯이 패턴의 마지막 캔들 고점 위에 빨간색 화살표와 하락 삼킴형 텍스트가 있습니다.
피어싱 라인과 다크 클라우드 커버 패턴:
- 피어싱 라인 패턴:
이 캔들은 강세 캔들이며 첫 번째 캔들이 약세 캔들이고 그 다음 약세 캔들보다 시가가 낮은 강세 캔들이 상승하여 첫 번째 약세 캔들의 중간점 위에서 종가를 형성하기 때문에 두 개의 캔들로 구성됩니다. 다음 그림은 이를 설명하는 그래프입니다:
이는 매도세의 통제 이후 매수세가 더 강해져 시장을 지배하고 있음을 나타냅니다. 즉, 개장 시 갭이 있었지만 매수세가 이전 약세 캔들의 중간점 위로 가격을 밀어 올릴 수 있었기 때문이고 이는 매도세에서 매수세로 전환된 것을 의미합니다.
- 다크 클라우드 커버 패턴:
피어싱 패턴과 반대되는 형태로 두 개의 캔들 구조로 이루어진 약세 패컨입니다. 첫 번째 캔들은 강세이고 그 뒤에는 개장 갭이 있는 약세 캔들이 첫 번째 강세 캔들의 중간점 아래에서 마감되는 패턴입니다. 다음은 이 형태의 그래프입니다:
이는 매수세의 통제 이후 매도자가 더 강해져 시장을 지배하고 있음을 나타냅니다. 즉 개장 시 갭이 있었지만 매도세가 이전 강세 캔들의 중간점 아래로 가격을 밀어내면서 매수세에서 매도세로의 전환을 의미합니다.
이러한 유형의 패턴을 감지하는 데 사용할 수 있는 프로그램을 만들려면 우리는 다음을 수행해야 합니다. 첫 번째 캔들의 시간과 가격(시간, 시가, 고가, 저가, 종가)과 두 번째 캔들의 가격(시가2, 고가2, 저가2, 종가2), 첫 번째 캔들의 캔들 크기(candleSize2) 및 첫 번째 캔들의 중간점(candleMidPoint2)을 정의해야 합니다. 프로그램이 이러한 값을 지속적으로 확인하고 서로 관련된 위치를 결정하고 강세 또는 약세에 따른 특정 조건에 따라 특정 신호를 반환해야 합니다.
다음은 이 프로그램을 만드는 전체 코드입니다:
//+------------------------------------------------------------------+ //| Piercing && Dark Cloud pattern detector.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ void OnTick() { getPiercing(); } int getPiercing() { datetime time=iTime(_Symbol,PERIOD_CURRENT,1); double open=iOpen(_Symbol,PERIOD_CURRENT,1); double high=iHigh(_Symbol,PERIOD_CURRENT,1); double low=iLow(_Symbol,PERIOD_CURRENT,1); double close=iClose(_Symbol,PERIOD_CURRENT,1); double open2=iOpen(_Symbol,PERIOD_CURRENT,2); double high2=iHigh(_Symbol,PERIOD_CURRENT,2); double low2=iLow(_Symbol,PERIOD_CURRENT,2); double close2=iClose(_Symbol,PERIOD_CURRENT,2); double candleSize2=high2-low2; double candleMidPoint2=high2-(candleSize2/2); if(open<close) { if(open2>close2) { if(open<low2) { if(close>candleMidPoint2&&close<high2) { createObj(time,low,217, clrGreen,"Piercing"); { return 1; } } } } } if(open>close) { if(open2<close2) { if(open>high2) { if(close<candleMidPoint2&&close>low2) { createObj(time,high,218, clrRed,"Dark Cloud"); { return -1; } } } } } return 0; } void createObj(datetime time, double price, int arrawCode, color clr, string txt) { string objName=" "; StringConcatenate(objName, "Signal@",time, "at",DoubleToString(price,_Digits),"(",arrawCode,")"); if(ObjectCreate(0,objName,OBJ_ARROW,0,time,price)) { ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrawCode); ObjectSetInteger(0,objName,OBJPROP_COLOR,clr); if(clr==clrGreen) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP); if(clr==clrRed) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM); } string candleName=objName+txt; if(ObjectCreate(0,candleName,OBJ_TEXT,0,time,price)) { ObjectSetString(0,candleName,OBJPROP_TEXT," "+txt); ObjectSetInteger(0,candleName,OBJPROP_COLOR,clr); } }
이 코드의 차이점
candleSize2 및 candleMidPoint2 정의하기
double candleSize2=high2-low2; double candleMidPoint2=high2-(candleSize2/2);
패턴의 조건
피어싱 라인 패턴의 경우:
마지막 캔들이 강세(open<close)이고 open2가 close2보다 크고 open이 low2보다 낮고 close가 candleMidPoint2보다 크면서 동시에 close가 high2보다 낮은 경우, 프로그램은 차트에 녹색 화살표와 패턴의 저점 아래에 "Piercing"이라는 텍스트가 있는 객체를 반환한 다음 함수를 종료해야 합니다.
if(open<close) { if(open2>close2) { if(open<low2) { if(close>candleMidPoint2&&close<high2) { createObj(time,low,217, clrGreen,"Piercing"); { return 1; } } } } }
다크 클라우드 커버 패턴의 경우:
마지막 캔들이 약세(오픈>종가)이고 open2가 close2보다 낮고 open이 high2보다 크고 close가 candleMidPoint2보다 낮고 동시에 close가 low2보다 큰 경우 프로그램에서 패턴의 고점 위에 빨간색 화살표와 "Dark Cloud"라는 텍스트가 있는 객체를 차트에 반환한 다음 함수를 종료해야 합니다.
if(open>close) { if(open2<close2) { if(open>high2) { if(close<candleMidPoint2&&close>low2) { createObj(time,high,218, clrRed,"Dark Cloud"); { return -1; } } } } }
이 코드를 컴파일하고 EA를 실행하면 테스트에서 다음 예제와 같은 신호를 얻을 수 있습니다:
- 피어싱 라인 패턴:
이전 차트에서 볼 수 있듯이 패턴의 저점 아래에 녹색 화살표와 피어싱 텍스트가 배치되어 있습니다.
- 다크 클라우드 커버 패턴:
이전 차트에서 볼 수 있듯이 패턴의 고점 위에 빨간색 화살표와 다크 클라우드 텍스트가 표시되어 있습니다.
세 개의 캔들 패턴
이 부분에서는 혼합 패턴에서 두 가지 패턴을 볼 수 있으며 별 패턴(아침, 저녁)과 세 개의 인사이드 패턴(위, 아래)이 있습니다.
별 패턴:
- 모닝 스타:
앞서 설명한 것과 같이 세 개의 캔들 구조입니다. 두 캔들 사이의 작은 캔들로 형성되며 첫 번째 캔들은 기다란 약세, 두 번째 캔들은 기다란 강세입니다. 다음은 이 형태의 그래프입니다:
이는 매도세의 통제에 의해 가격이 하락한 후 매수자가 시장을 장악하고 가격을 상승시키면서 매도세에서 매수세로 힘이 이동하는 것을 의미합니다.
- 이브닝 스타 패턴:
이는 매도세가 시장을 통제하는 것을 의미하며 매수세에 의한 랠리 후 가격을 낮추면서 매수에서 매도로 힘이 이동하는 것을 의미합니다.
이러한 패턴을 감지하는 데 사용할 수 있는 프로그램을 만들려면 마지막 캔들의 시간과 가격, 마지막 캔들의 이전 두 캔들의 가격 데이터, 마지막 세 캔들의 캔들 크기를 정의하고 이를 서로 비교하여 서로 관련된 위치를 결정하여 특정 조건에 따라 특정 신호를 얻어야 합니다.
다음은 이 프로그램을 만드는 전체 코드입니다:
//+------------------------------------------------------------------+ //| Star pattern detector.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" void OnTick() { getStar(0.5); } int getStar(double middleCandleRatio) { datetime time=iTime(_Symbol,PERIOD_CURRENT,1); double open=iOpen(_Symbol,PERIOD_CURRENT,1); double high=iHigh(_Symbol,PERIOD_CURRENT,1); double low=iLow(_Symbol,PERIOD_CURRENT,1); double close=iClose(_Symbol,PERIOD_CURRENT,1); double open2=iOpen(_Symbol,PERIOD_CURRENT,2); double high2=iHigh(_Symbol,PERIOD_CURRENT,2); double low2=iLow(_Symbol,PERIOD_CURRENT,2); double close2=iClose(_Symbol,PERIOD_CURRENT,2); double open3=iOpen(_Symbol,PERIOD_CURRENT,3); double high3=iHigh(_Symbol,PERIOD_CURRENT,3); double low3=iLow(_Symbol,PERIOD_CURRENT,3); double close3=iClose(_Symbol,PERIOD_CURRENT,3); double candleSize=high-low; double candleSize2=high2-low2; double candleSize3=high3-low3; if(open<close) { if(open3>close3) { if(candleSize2<candleSize*middleCandleRatio && candleSize2<candleSize3*middleCandleRatio) { createObj(time,low,217, clrGreen,"Morning Star"); { return 1; } } } } if(open>close) { if(open3<close3) { if(candleSize2<candleSize*middleCandleRatio && candleSize2<candleSize3*middleCandleRatio) { createObj(time,high,218, clrRed,"Evening Star"); { return -1; } } } } return 0; } void createObj(datetime time, double price, int arrawCode, color clr, string txt) { string objName=" "; StringConcatenate(objName, "Signal@",time, "at",DoubleToString(price,_Digits),"(",arrawCode,")"); if(ObjectCreate(0,objName,OBJ_ARROW,0,time,price)) { ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrawCode); ObjectSetInteger(0,objName,OBJPROP_COLOR,clr); if(clr==clrGreen) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP); if(clr==clrRed) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM); } string candleName=objName+txt; if(ObjectCreate(0,candleName,OBJ_TEXT,0,time,price)) { ObjectSetString(0,candleName,OBJPROP_TEXT," "+txt); ObjectSetInteger(0,candleName,OBJPROP_COLOR,clr); } }
이 코드의 차이점
middleCandleRatio에 매개 변수를 사용하여 (getStar) 생성하기
int getStar(double middleCandleRatio)
마지막 캔들의 시간 변수와 가격 데이터(시가, 고가, 저가, 종가), 마지막 세 캔들의 캔들 크기(candlesize, candleSize2, and candleSize3)를 생성합니다.
datetime time=iTime(_Symbol,PERIOD_CURRENT,1); double open=iOpen(_Symbol,PERIOD_CURRENT,1); double high=iHigh(_Symbol,PERIOD_CURRENT,1); double low=iLow(_Symbol,PERIOD_CURRENT,1); double close=iClose(_Symbol,PERIOD_CURRENT,1); double open2=iOpen(_Symbol,PERIOD_CURRENT,2); double high2=iHigh(_Symbol,PERIOD_CURRENT,2); double low2=iLow(_Symbol,PERIOD_CURRENT,2); double close2=iClose(_Symbol,PERIOD_CURRENT,2); double open3=iOpen(_Symbol,PERIOD_CURRENT,3); double high3=iHigh(_Symbol,PERIOD_CURRENT,3); double low3=iLow(_Symbol,PERIOD_CURRENT,3); double close3=iClose(_Symbol,PERIOD_CURRENT,3); double candleSize=high-low; double candleSize2=high2-low2; double candleSize3=high3-low3;
패턴의 조건
모닝스타 패턴의 경우:
마지막 캔들이 강세(open<close), 세 번째 캔들이 약세(open3>close3), candleSize2가 0.5인 캔들 사이즈의 middleCandleRatio보다 낮고 동시에 candleSize2가 candleSize3의 middleCandleRatio보다 낮은 경우, 프로그램은 패턴의 저점 아래에 녹색 화살표와 "Morning Star" 텍스트의 객체를 반환한 다음 함수를 종료해야 합니다.
if(open<close) { if(open3>close3) { if(candleSize2<candleSize*middleCandleRatio && candleSize2<candleSize3*middleCandleRatio) { createObj(time,low,217, clrGreen,"Morning Star"); { return 1; } } } }
이브닝 스타의 경우:
마지막 캔들이 약세(오픈>종가), 세 번째 캔들이 강세(오픈3<종가3), candleSize2가 캔들 사이즈의 middleCandleRatio인 0.5보다 낮고 동시에 candleSize2가 candleSize3의 middleCandleRatio보다 낮은 경우 프로그램은 패턴의 고점 위에 빨간색 화살표와 "Evening Star"란 텍스트 객체를 반환한 다음 함수를 종료해야 합니다.
if(open>close) { if(open3<close3) { if(candleSize2<candleSize*middleCandleRatio && candleSize2<candleSize3*middleCandleRatio) { createObj(time,high,218, clrRed,"Evening Star"); { return -1; } } } }
이 코드를 오류 없이 컴파일하고 EA를 실행하면 테스트에서 다음 예제와 같은 신호를 얻을 수 있습니다:
- 모닝 스타:
감지된 패턴 아래 차트에서 원하는 객체의 신호가 있음을 알 수 있습니다.
- 이브닝 스타:
감지된 패턴 아래 차트에서 원하는 객체의 신호가 있음을 알 수 있습니다.
스타 패턴에 대한 참고 사항으로 동일한 패턴 형성이 가운데 작은 캔들과 간격이 있으므로 동일한 패턴을 얻으려면 여러분은 코드에 이를 추가 조건으로 추가할 수 있습니다.
쓰리 인사이드 패턴:
- 쓰리 인사이드 업:
또한 3개의 캔들 패턴으로 첫 번째 캔들은 긴 약세, 두 번째 캔들은 첫 번째 캔들 내부에서 거래되는 작은 상승 캔들, 세 번째 캔들은 첫 번째 캔들의 고점 위에서 마감되는 긴 상승 캔들입니다. 다음은 이 패턴의 그래프입니다.
그 중요도에 따라 매수세의 통제에 의한 잠재적 강세를 나타냅니다.
- 쓰리 인사이드 다운:
또한 3 캔들 패턴으로 첫 번째 캔들은 긴 강세 캔들, 두 번째 캔들은 첫 번째 캔들 내부에서 거래되는 작은 약세 캔들, 세 번째 캔들은 첫 번째 캔들의 저점 아래에서 마감되는 긴 약세 캔들입니다. 다음은 이 패턴의 그래프입니다.
//+------------------------------------------------------------------+ //| Three inside pattern detector.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" void OnTick() { getthreeInside(); } int getthreeInside() { datetime time=iTime(_Symbol,PERIOD_CURRENT,1); double open=iOpen(_Symbol,PERIOD_CURRENT,1); double high=iHigh(_Symbol,PERIOD_CURRENT,1); double low=iLow(_Symbol,PERIOD_CURRENT,1); double close=iClose(_Symbol,PERIOD_CURRENT,1); double open2=iOpen(_Symbol,PERIOD_CURRENT,2); double high2=iHigh(_Symbol,PERIOD_CURRENT,2); double low2=iLow(_Symbol,PERIOD_CURRENT,2); double close2=iClose(_Symbol,PERIOD_CURRENT,2); double open3=iOpen(_Symbol,PERIOD_CURRENT,3); double high3=iHigh(_Symbol,PERIOD_CURRENT,3); double low3=iLow(_Symbol,PERIOD_CURRENT,3); double close3=iClose(_Symbol,PERIOD_CURRENT,3); if(open3>close3) { if(open2<close2) { if(open2>low3&&close2<high3) { if(open<close&&open>open2&&open<close2) { if(close>high3) { createObj(time,low,217, clrGreen,"3 Inside Up"); { return 1; } } } } } } if(open3<close3) { if(open2>close2) { if(open2<high3&&close2>low3) { if(open>close&&open<open2&&open>close2) { if(close<low3) { createObj(time,high,218, clrRed,"3 Inside Down"); { return -1; } } } } } } return 0; } void createObj(datetime time, double price, int arrawCode, color clr, string txt) { string objName=" "; StringConcatenate(objName, "Signal@",time, "at",DoubleToString(price,_Digits),"(",arrawCode,")"); if(ObjectCreate(0,objName,OBJ_ARROW,0,time,price)) { ObjectSetInteger(0,objName,OBJPROP_ARROWCODE,arrawCode); ObjectSetInteger(0,objName,OBJPROP_COLOR,clr); if(clr==clrGreen) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_TOP); if(clr==clrRed) ObjectSetInteger(0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM); } string candleName=objName+txt; if(ObjectCreate(0,candleName,OBJ_TEXT,0,time,price)) { ObjectSetString(0,candleName,OBJPROP_TEXT," "+txt); ObjectSetInteger(0,candleName,OBJPROP_COLOR,clr); } }
이 코드의 차이점은 패턴의 조건입니다.
쓰리 인사이드 업의 경우
if(open3>close3) { if(open2<close2) { if(open2>low3&&close2<high3) { if(open<close&&open>open2&&open<close2) { if(close>high3) { createObj(time,low,217, clrGreen,"3 Inside Up"); { return 1; } } } } } }
쓰리 인사이드 다운의 경우
if(open3<close3) { if(open2>close2) { if(open2<high3&&close2>low3) { if(open>close&&open<open2&&open>close2) { if(close<low3) { createObj(time,high,218, clrRed,"3 Inside Down"); { return -1; } } } } } }
이 코드를 오류 없이 컴파일하고 EA를 실행하면 다음 예제와 같은 신호를 얻을 수 있습니다:
- 쓰리 인사이드 업:
차트에서 볼 수 있듯이 쓰리 인사이드 업의 신호가 있습니다.
- 쓰리 인사이드 다운:
차트에서 볼 수 있듯이 쓰리 인사이드 다운의 신호가 있습니다.
결론
여러분은 이 글의 주제를 통해 단일 캔들, 이중 캔들, 3개 캔들 패턴 등 다양한 형태의의 캔들 패턴을 감지하는 코드를 작성하는 방법에 대한 아이디어를 얻었을 것입니다:
- 신호 캔들 패턴: 우리는 도지 패턴과 해머 패턴을 감지하는 방법을 배웠습니다.
- 듀얼 캔들 패턴: 삼킴, 피어싱 라인, 다크 클라우드 커버 패턴을 감지하는 방법을 알아보았습니다.
- 세 개의 캔들 패턴: 스타 패턴과 세 개의 인사이드 패턴을 감지할 수 있는 프로그램을 만드는 방법을 배웠습니다.
이 글이 더 나은 인사이트를 얻으려는 여러분에게 도움이 되었기를 바랍니다.
MetaQuotes 소프트웨어 사를 통해 영어가 번역됨
원본 기고글: https://www.mql5.com/en/articles/12385


