본문 바로가기
Investment/System Trading

[SytemTrading] ZigZag 패턴 알고리즘

by qWooWp 2022. 10. 6.
반응형

시계열 데이터에서 파동 또는 패턴을 분석하기 위한 알고리즘중에 아래와 같은 코드가 있어서 분석해 보았다. 

이런 자료들은 메타트레이더 관련 사이트에서 얻을 수 있는 것 같으니 참고 바란다. 

https://www.mql5.com/en/articles/4502

 

Developing the oscillator-based ZigZag indicator. Example of executing a requirements specification

The article demonstrates the development of the ZigZag indicator in accordance with one of the sample specifications described in the article "How to prepare Requirements Specification when ordering an indicator". The indicator is built by extreme values d

www.mql5.com

이 코드의 목적은 효율적으로 시계열 데이터의 저점과 고점을 이루는 파동을 계산하기 위함이다. 

 

아래 알고리즘에서 주요 고려 사항으로 생각 되는 포인트는 2가지 이다. 

 

1. ZIGZAG 턴을 하기 위한 조건으로고점이 발생했을 때 캔들의 저점이 고점대비해서 얼마나 떨어졌을 때를 기준으로 판단할 것이냐 / 저점은 반대로 저점이 발생한 후 캔들의 고점이 저점 대비해서 얼마나 떨어질 때를 기준으로 잡냐가 될 것이다. 

즉 dH = H * Point

 

2. 시계열 데이터의 경우 최신 가격 데이터가 업데이트 되었을 때 먼저 처리가 될 수 있게 하자

 

extern int H = 33;
extern int MinBars = 0;
extern bool SaveData = false;

double dH;
bool UpZ;
double CurMax,CurMin;
int CurMaxBar,CurMinBar;
double Top,Bot;
double SumR,SumT,SumTD;
int ZCnt;
int h = -1;
string FileName;

int PreBars;
datetime BarTime;
int StartPos;
int pos;

double HZZ[]; // 결과가 들어가는 곳 

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init() {
  if (SaveData) FileName = "HZZ.txt";

  SetIndexBuffer(0,HZZ);
  SetIndexStyle(0,DRAW_SECTION,EMPTY,2);
  SetIndexEmptyValue(0,0.0);

  return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit() {
  //Comment("");
  if (SaveData) FileClose(h);
  return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator reset function                                  |
//+------------------------------------------------------------------+
int Reset() {
  if (SaveData) {
    FileClose(h);
    h = FileOpen(FileName,FILE_CSV|FILE_WRITE,';');
  }

  // 제일 적은 Bars 가 0이면 시작 위치는 현재 바개수 -1 해서 시작
  if (MinBars==0) 
    StartPos = Bars-1; // 마지막 데이터 부터 시작
  else 
    StartPos = MinBars; // 중간 데이터 부터 시작

  PreBars = 0;
  BarTime = 0;
  dH = H*Point; // 33 * Point
  UpZ = true;
  CurMaxBar = 1;
  CurMinBar = 1;
  CurMax = High[StartPos];
  CurMin = Low[StartPos];
  Top = CurMin+dH;
  Bot = CurMax-dH;
  ZCnt = 0;
  SumR = 0.0;
  SumT = 0.0;
  SumTD = 0.0;
  StartPos++;
  return(StartPos);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start() {

  // 캔들의 개수가 이전과 동일하다면 처리하지 않는다. 
  // [TODO] 최신 캔들의 데이터가 변경이 되었다면 처리를 해야 될 수 있다. 
  if (Bars == PreBars) 
    return(0);

  if (Bars < MinBars) {
    Console.WriteLine("차크 바 개수가 부족한 조건입니다.")    
    return(0);
  }  

  // 현재 바 갯수 - 이전 바 갯수가 1이면 새로운 바가 생긴 것이고, 시간이 BarTime 과 같다면
  if (Bars-PreBars == 1 && BarTime==Time[1]) 
    StartPos = 1;     // 시작 위치는 1
  else 
    StartPos = Reset(); // 아니면 리셋

  PreBars = Bars;  
  BarTime=Time[0];
  
  for (pos=StartPos; pos>0 ;pos--) {
    
    HZZ[pos] = 0;

    if (UpZ) {
      
      if (High[pos]>CurMax) {
        CurMax = High[pos];   // 최고가격 업데이트 
        CurMaxBar = Bars-pos; // 
        Bot = CurMax - dH;
      } else {
        // 고점이 낮아 질 때 저가가 최고값 대비 dH 만큼보다 작아진다면 
        if (Low[pos]<Bot) {
          ZCnt++; 
          if (ZCnt > 3) { // ZigZag 가 3개 이상 되엇다면 
            SumR += CurMax-CurMin; // 상승선의 최대 최소 값의 차이 누적
            SumT += CurMaxBar-CurMinBar; // 중간 바의 갯수 누적
            SumTD += Bars-pos-CurMaxBar; // 

            //if (SaveData) FileWrite(h,Time[Bars-CurMaxBar],NormalizeDouble(CurMax,Digits));
          }

          UpZ = false; // 방향 전환
          HZZ[Bars-CurMaxBar]=CurMax; // ZIGZAG 위치에 최고 가격 기록 
          CurMinBar = Bars-pos;       // 판단된 위치 가 저가일 수 도 있기 때문에 최저가 바 업데이트 
          CurMin = Low[pos];          // 최저가 없데이트 
          Top = CurMin+dH;            // 다시 상승 반전하기 위한 Threshold 값 설정
        }
      }
    }  else {

      if (Low[pos]<CurMin) {        // 최저가 갱신시에 위치
        CurMinBar = Bars-pos;
        CurMin = Low[pos];
        Top = CurMin+dH;
      } else {

        if (High[pos]>Top) {
          ZCnt++;
          if (ZCnt > 3) { // ZigZag 가 3개 이상 되엇다면 
            SumR += CurMax-CurMin;
            SumT += CurMinBar-CurMaxBar;
            SumTD += Bars-pos-CurMinBar;
            //if (SaveData) FileWrite(h,Time[Bars-CurMinBar],NormalizeDouble(CurMin,Digits));
          }

          UpZ = true;
          HZZ[Bars-CurMinBar]=CurMin;
          CurMaxBar = Bars-pos;
          CurMax = High[pos];
          Bot = CurMax-dH;
        }
      }
    }
  }  //  pos=StartPos;pos>0;pos--) 

  if (ZCnt > 3) {
    int NZ = ZCnt-3;
    // 발견된 선분의  평균 변동폭, 
    // 발견된 선분의 평균 바카운트 
    // 
    //Comment("Средни?размах: ",SumR/NZ,", Сред?? длительность: ",SumT/NZ,", Сред?? задержка: ",SumTD/NZ);  
  }
  return(0);
}

 

위 코드를 C# 으로 적용하여 아래와 같은 결과를 얻을 수 있었다. 

반응형

댓글