데이터 액세스 구성

이 섹션에서는 가격 데이터(시계열)의 획득, 저장 및 요청과 관련된 질문을 고려합니다.

트레이드 서버에서 데이터 수신

MetaTrader 5 터미널에서 가격 데이터를 이용할 수 있으려면 먼저 데이터를 받아 처리해야 합니다. 데이터를 수신하려면 MetaTrader 5 트레이드 서버에 연결해야 합니다. 데이터는 터미널의 요청에 따라 서버에서 분 막대의 팩킹된 블록 형태로 수신됩니다.

데이터에 대한 서버 참조 메커니즘은 요청이 어떻게 시작되었는지에 따라 달라지지 않습니다. 사용자가 차트나 MQL5 언어로 프로그램을 탐색할 때 그렇습니다.

중간 데이터 저장

서버에서 수신된 데이터는 자동으로 압축이 해제되고 HCC 중간 형식으로 저장됩니다. 각 심볼의 데이터는 다음과 같은 별도의 폴더에 기록됩니다: terminal_directory\bases\server_name\history\symbol_name. 예를 들어, MetaQuotes-Demo 서버에서 수신된 EURUSD의 데이터는 다음 위치에 저장됩니다. terminal_directory\bases\MetaQuotes-Demo\history\EURUSD\.

데이터는 확장자가 .hcc인 파일에 기록됩니다. 각 파일에는 1년 동안 분 막대의 데이터가 저장됩니다. 예를 들어, EURUSD 폴더에 있는 2009.hcc 파일은 2009년도의 EURUSD의 분 막대를 포함합니다. 이러한 파일은 모든 기간에 대한 가격 데이터를 준비하는 데 사용되며 직접 액세스하기 위한 것은 아닙니다.

중간 데이터 중 필요한 시간에 데이터 가져오기

중간 HCC 파일은 HC 형식의 요청된 기간에 대한 건물 가격 데이터의 데이터 소스로 사용됩니다. HC 형식의 데이터는 빠른 액세스를 위해 최대한으로 준비된 시계열입니다. 차트 또는 MQL5 프로그램의 요청에 따라 생성됩니다. 데이터 볼륨은 "차트의 최대 막대" 매개 변수의 값을 초과하면 안 됩니다. 데이터는 나중에 사용할 수 있도록 hc 확장자로 파일에 저장됩니다.

리소스를 절약하기 위해 필요한 경우에만 일정 기간의 데이터를 RAM에 저장하고 저장합니다. 오랫동안 호출하지 않으면 RAM에서 해제되어 파일에 저장됩니다. 각 기간에 대해 데이터는 다른 기간에 대한 준비 데이터가 있는지 여부에 관계없이 준비됩니다. 데이터 형성 및 액세스 규칙은 모든 기간 동안 동일합니다. 즉, HCC에 저장된 단위 데이터가 1분(M1)이라는 사실에도 불구하고, HCC 데이터의 가용성은 동일한 볼륨에서 HC와 같은 M1 기간에 데이터 가용성을 의미하는 것은 아닙니다.

서버로부터 새로운 데이터를 수신하면 모든 기간 동안 HC 형식의 중고 가격 데이터의 자동 업데이트가 요구됩니다. 또한 계산을 위한 입력 데이터로 암시적으로 사용하는 모든 지표가 재계산됩니다.

매개 변수 "차트의 최대 막대"

"차트의 최대 막대" 매개 변수는 차트, 지표 및 mql5 프로그램에서 사용할 수 있는 HC 형식의 막대 수를 제한합니다. 이는 사용 가능한 모든 기간 동안 유효하며 먼저 시스템 리소스를 절약하는 데 사용됩니다.

이 매개 변수의 큰 값을 설정할 때, 작은 시간대에 대한 깊은 기록 가격 데이터를 사용할 수 있는 경우 시계열 및 지표 버퍼를 저장하는 데 사용되는 메모리가 수백 메가바이트가 되어 클라이언트 터미널 프로그램의 RAM 제한에 도달할 수 있다는 점을 기억해야 합니다(MS Windows 32비트 애플리케이션의 경우 2Gb).

클라이언트 터미널을 재시작한 후 "차트의 최대 막대" 변경 내용이 적용됩니다. 이 매개 변수를 변경하면 서버를 자동으로 참조하여 추가 데이터를 확인하거나 시계열의 추가 막대를 형성하지 않습니다. 서버에서 추가 가격 데이터를 요청하고, 데이터가 없는 영역으로 차트 스크롤하거나 mql5 프로그램에서 데이터를 요청하는 경우 새로운 제한 사항을 고려하여 시계열을 업데이트합니다.

서버에서 요청한 데이터 볼륨은 "차트의 최대 막대" 매개 변수를 고려하여 이 기간 동안 필요한 막대 수에 해당합니다. 이 매개 변수에 의해 설정된 제한은 엄격하지 않으며 경우에 따라 특정 기간 동안 사용 가능한 막대 수가 현재 매개 변수 값보다 약간 많을 수 있습니다.

데이터 가용성

HCC 형식 또는 HC 형식 사용을 위해 준비된 데이터가 있다고 해서 이러한 데이터가 차트에 표시되거나 MQL5 프로그램에 사용될 수 있는 절대 가용성을 나타내는 것은 아닙니다.

mql5 프로그램에서 가격 데이터 또는 지표 값에 액세스하는 경우 특정 시점 또는 특정 시점부터의 가용성이 보장되지 않는다는 점을 기억해야 합니다. 리소스를 절약하기 위해 mql5 프로그램에 필요한 전체 데이터 복사본이 MetaTrader 5에 저장되지 않고 터미널 데이터베이스에 대한 직접 액세스만 제공된다는 사실과 연결됩니다.

모든 기간에 대한 가격 기록은 HCC 형식의 공통 데이터로 구축되며, 서버의 모든 데이터 업데이트는 모든 기간에 대한 데이터 업데이트와 지표 재계산으로 이어집니다. 이러한 데이터 액세스는 잠시 전이라도 종료할 수 있습니다.

터미널 데이터와 서버 데이터의 동기화 #

mql5 프로그램은 어떤 심볼과 시간대의 데이터도 호출할 수 있기 때문에 필요한 시계열 데이터가 터미널에 아직 형성되지 않았거나 필요한 가격 데이터가 트레이드 서버와 동기화되지 않았을 가능성이 있습니다. 이 경우에는 지연 시간을 예측하기가 어렵습니다.

"아무것도 하지 않는" 루프를 사용하는 알고리즘은 최상의 솔루션이 아닙니다. 이 경우 스크립트만 예외입니다. 스크립트에는 이벤트 처리 기능이 없기 때문에 다른 알고리즘을 선택할 수 없기 때문입니다. 커스텀 지표의 경우, 그러한 알고리즘은 모든 지표의 계산 종료 및 심볼의 가격 데이터의 다른 취급으로 이어지기 때문에 다른 "아무 것도 하지 않는" 루프는 강력히 권장하지 않습니다.

Expert Advisor 및 지표의 경우 이벤트 모델을 사용하여 처리하는 것이 좋습니다. OnTick() 또는 OnCalculate() 이벤트를 처리하는 동안 필요한 시계열에 대한 데이터 수신이 실패한 경우 다음 핸들러 호출 시 액세스 가용성에 의존하여 이벤트 핸들러를 종료해야 합니다.

기록 추가를 위한 스크립트 예제

트랜잭션 서버에서 선택한 심볼에 대한 기록을 수신하기 위한 요청을 실행하는 스크립트의 예를 살펴보겠습니다. 이 스크립트는 선택한 심볼의 차트에서 실행하기 위한 것이며, 위에서 언급한 바와 같이 가격 데이터는 포장된 1분 데이터로 트레이드 서버에서 수신되고, 여기에서 사전 정의된 시계열은 구성되기 때문에 기간은 문제가 되지 않습니다.

데이터 수신과 관련된 모든 작업에 별도의 CheckLoadHistory 함수(symbol, timeframe, start_date):

int CheckLoadHistory(string symbol,ENUM_TIMEFRAMES period,datetime start_date)
  {
  }

CheckLoadHistory() 함수는 모든 프로그램(Expert Advisor, 스크립트 또는 지표)에서 호출할 수 있는 범용 기능으로 설계되었으므로 필요한 가격 기록의 시작을 나타내려면 심볼 이름, 기간 및 시작 날짜의 세 가지 입력 매개 변수가 필요합니다.

누락된 기록을 요청하기 전에 함수 코드에 필요한 점검을 삽입하십시오. 먼저 심볼 이름과 주기 값이 올바른지 확인해야 합니다:

   if(symbol==NULL || symbol==""symbol=Symbol();
   if(period==PERIOD_CURRENT)     period=Period();

그런 다음 해당 심볼이 MarketWatch 창에서 사용 가능한지 확인합니다. 즉, 요청을 트레이드 서버로 전송할 때 심볼의 기록을 사용할 수 있습니다. If there is no such a symbol in MarketWatch, add it using the SymbolSelect() function.

   if(!SymbolInfoInteger(symbol,SYMBOL_SELECT))
     {f
      if(GetLastError()==ERR_MARKET_UNKNOWN_SYMBOLreturn(-1);
      SymbolSelect(symbol,true);
     }

이제 표시된 심볼/주기에 대해 사용 가능한 기록의 시작 날짜를 수신해야 합니다. CheckLoadHistory()로 전달된 입력 매개 변수 시작 날짜의 값이 사용 가능한 기록 내에 있을 수 있습니다. 그러면 트레이드 서버에 대한 요청이 필요하지 않습니다. 순간 현재 기호 주기의 첫 번째 날짜를 얻기 위해 SeriesInfoInteger() 함수를 SERIES_FIRSTDATE 변경자와 함께 사용합니다.

   SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date);
   if(first_date>0 && first_date<=start_date) return(1);

다음으로 중요한 검사는 함수가 호출되는 프로그램의 유형을 확인하는 것입니다. 지표에서 동일한 기간의 시계열 업데이트 요청을 보내는 것은 바람직하지 않습니다. 지표와 동일한 심볼 주기에 대한 데이터 요청이 바람직하지 않은 것은 지표가 작동하는 동일한 스레드에서 기록 데이터 업데이트가 수행된다는 사실에 의해 조절됩니다. 따라서 교착 상태가 발생할 가능성이 높습니다. 이를 확인하려면 MQL5InfoInteger() 함수를 MQL5_PROGRAM_TYPE 변경자와 함께 사용하십시오.

   if(MQL5InfoInteger(MQL5_PROGRAM_TYPE)==PROGRAM_INDICATOR && Period()==period && Symbol()==symbol)
      return(-4);

모든 검사가 성공적으로 통과되었다면 거래 서버를 참조하지 않고 마지막 검사를 시도합니다. 먼저 HCC 형식의 분 데이터를 사용할 수 있는 시작 날짜를 알아보겠습니다. SERIES_TERMINAL_FIRSTDATE 변경자와 함께 SeriesInfoInteger() 함수를 사용하여 이 값을 요청하고 start_date 매개 변수의 값과 다시 비교합니다.

   if(SeriesInfoInteger(symbol,PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_date))
     {
      //--- 시계열을 빌드하기 위해 로드된 데이터가 있습니다.
      if(first_date>0)
        {
         //--- 시계열 강제 생성
         CopyTime(symbol,period,first_date+PeriodSeconds(period),1,times);
         //--- 날짜 점검
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date))
            if(first_date>0 && first_date<=start_date) return(2);
        }
     }

모든 검사 후에도 실행 스레드가 CheckLoadHistory() 함수의 본문에 남아 있다면 거래 서버에 누락된 가격 데이터를 요청할 필요가 있음을 의미합니다. 먼저 TerminalInfoInteger() 함수를 사용하여 "차트의 최대 막대" 값을 반환합니다.:

  int max_bars=TerminalInfoInteger(TERMINAL_MAXBARS);

추가 데이터 요청을 방지하기 위해 필요합니다. 그런 다음 SERIES_SERVER_FIRSTDATE 변경자와 함께 이미 알려진 SeriesInfoInteger() 함수를 사용하여 거래 서버의 심볼 내역에서 첫 번째 날짜를 찾습니다.

   datetime first_server_date=0;
   while(!SeriesInfoInteger(symbol,PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date) && !IsStopped())
      Sleep(5);

변수가 값을 수신하거나 사용자가 루프 실행을 종료할 때까지 5밀리초의 짧은 지연으로 함수가 루프에서 호출됩니다(IsStopped()true를 반환합니다, 이러한 경우). 거래 서버에 가격 데이터를 요청하는 시작 날짜의 정확한 값을 표시하겠습니다.

   if(first_server_date>start_date) start_date=first_server_date;
   if(first_date>0 && first_date<first_server_date)
      Print("주의: 첫번째 서버 날짜 ",first_server_date," for ",
symbol," 첫 번째 시리즈 날짜와 일치하지 않음 ",first_date);

서버의 시작 날짜 first_server_date가 HCC 형식의 심볼 시작 날짜 first_date 보다 낮으면 해당 항목이 저널에 출력됩니다.

이제 우리는 거래 서버에 누락된 가격 데이터를 요청할 준비가 되었습니다. 루프 형태로 요청하고 차체 작성 시작:

   while(!IsStopped())
     {
      //1. HHC로 재구성된 시계열과 중간 기록 사이의 동기화를 기다립니다
      //2. 이 시계열의 현재 개수의 막대를 수령
      //    막대가 Max_bars_in_chart보다 크면, 종료할 수 있고, 작업이 끝난 것입니다
      //3. re-built 시계열에서 시작 날짜를 first_date로 가져와서 start_date와 비교
      //    first_date가 start_date보다 낮으면 종료할 수 있으며, 작업이 끝난 것입니다
      //4. 서버로부터 내역의 새로운 부분 요청 - 사용 가능한 마지막 막대 번호 'bars'부터 시작하는 100 bar
     }

처음 세 가지는 이미 알려진 방법으로 구현됩니다.

   while(!IsStopped())
     {
      //--- 1.시계열 재구성 프로세스가 끝날 때까지 대기
      while(!SeriesInfoInteger(symbol,period,SERIES_SYNCHRONIZED) && !IsStopped())
         Sleep(5);
      //--- 2.막대가 몇 개나 있는지 요청
      int bars=Bars(symbol,period);
      if(bars>0)
        {
         //--- 차트에 그릴 수 있는 막대 두 개 이상, 종료
         if(bars>=max_bars) return(-2); 
         //--- 3. 시계열의 현재 시작 날짜를 반환
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date))
            // 시작 날짜가 요청된 날짜보다 이전이며 작업이 완료됨
            if(first_date>0 && first_date<=start_date) return(0);
        }
      //4. 서버로부터 내역의 새 파트 요청 - 사용 가능한 마지막 막대 번호 'bars'부터 시작하는 100 bar
     }

마지막 네 번째 지점은 왼쪽 - 요청 내역. 서버를 직접 참조할 수는 없지만, HCC 형식의 기록이 충분하지 않을 경우 Copy-function가 서버에 요청을 자동으로 전송합니다. first_date 변수에서 첫 번째 시작 날짜의 시간은 요청 실행 정도를 평가하는 간단하고 자연스러운 기준이기 때문에, 가장 쉬운 방법은 CopyTime() 함수를 사용하는 것입니다.

시계열에서 데이터를 복사하는 기능을 호출할 때는 start 파라미터(가격 데이터를 복사해야 하는 막대의 번호)가 항상 사용 가능한 터미널 기록 내에 있어야 합니다. 막대가 100개뿐이라면 인덱스 500을 사용하여 막대 300개를 복사하는 것은 의미가 없습니다. 이러한 요청은 오류로 인식되며 처리되지 않습니다. 즉, 트레이드 서버에서 추가 기록이 로드되지 않습니다.

그것이 바로 우리가 막대 인덱스를 가진 막대부터 100개 그룹으로 막대를 복하는 이유입니다. 이를 통해 트레이드 서버에서 누락된 이력을 원활하게 로드할 수 있습니다. 실제로 서버가 크기 초과 기록을 전송하는 동안 요청된 100개보다 조금 더 많은 막대가 로드됩니다.

   int copied=CopyTime(symbol,period,bars,100,times);

복사 작업이 끝나면 복사된 요소의 수를 분석해야 합니다. 시도가 실패하면 복제된 값이 null이 되고 fail_cnt 카운터의 값이 1 증가합니다. 100회 시도 실패 후 기능 작동이 중지됩니다.

int fail_cnt=0;
...
   int copied=CopyTime(symbol,period,bars,100,times);
   if(copied>0)
     {
      //--- 데이터 점검
      if(times[0]<=start_date)  return(0);  // 복사된 값이 더 작고 준비됨
      if(bars+copied>=max_bars) return(-2); // 막대가 차트에서 그릴 수 있는 것보다 많습니다. 준비되었습니다.
      fail_cnt=0;
     }
   else
     {
      //--- 연속해서 실패한 시도가 100회 이하
      fail_cnt++;
      if(fail_cnt>=100) return(-5);
      Sleep(10);
     }
 

따라서 각 실행 순간의 현재 상황에 대한 올바른 처리가 기능에서 구현될 뿐만 아니라 추가 정보를 얻기 위해 CheckLoadHistory() 함수를 호출한 후 처리할 수 있는 종료 코드도 반환됩니다. 예를 들어 다음과 같습니다:

   int res=CheckLoadHistory(InpLoadedSymbol,InpLoadedPeriod,InpStartDate);
   switch(res)
     {
      case -1 : Print("알수없는 심볼 ",InpLoadedSymbol);                     break;
      case -2 : Print("차트에서 그릴 수 있는 것보다 더 많은 요청 막대"); break;
      case -3 : Print("사용자에 의해 실행이 중단됨");                          break;
      case -4 : Print("지표가 자체 데이터를 로드해서는 안됨.");                break;
      case -5 : Print("로딩 실패");                                     break;
      case  0 : Print("모든 데이터 로드됨");                                    break;
      case  1 : Print("시계열로 이미 사용 가능한 데이터면 충분함");    break;
      case  2 : Print("시계열은 사용 가능한 터미널 데이터로 구축됨");   break;
      default : Print("실행 결과 정의되지 않음");
     }

함수의 전체 코드는 요청 결과를 처리하는 모든 데이터에 대한 올바른 액세스 구성을 보여 주는 스크립트의 예에서 확인할 수 있습니다.

Code:

//+------------------------------------------------------------------+
//|                                              TestLoadHistory.mq5 |
//|                         Copyright 2000-2024, MetaQuotes Ltd. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2009, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.02"
#property script_show_inputs
//--- 입력 매개 변수
input string          InpLoadedSymbol="NZDUSD";   // Symbol to be load
input ENUM_TIMEFRAMES InpLoadedPeriod=PERIOD_H1;  // Period to be loaded
input datetime        InpStartDate=D'2006.01.01'; // Start date
//+------------------------------------------------------------------+
//| 스크립트 프로그램 시작 함수                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print("Start load",InpLoadedSymbol+","+GetPeriodName(InpLoadedPeriod),"from",InpStartDate);
//---
   int res=CheckLoadHistory(InpLoadedSymbol,InpLoadedPeriod,InpStartDate);
   switch(res)
     {
      case -1 : Print("알수없는 심볼 ",InpLoadedSymbol);             break;
      case -2 : Print("요청된 막대가 차트의 최대 막대를 초과함"); break;
      case -3 : Print("프로그램 정지됨");                        break;
      case -4 : Print("지표가 자체 데이터를 로드해서는 안됨");      break;
      case -5 : Print("로딩 실패");                                break;
      case  0 : Print("로딩 성공");                                  break;
      case  1 : Print("이전에 로드됨");                          break;
      case  2 : Print("이전에 로드되고 빌트됨");                break;
      default : Print("알수없는 결과");
     }
//---
   datetime first_date;
   SeriesInfoInteger(InpLoadedSymbol,InpLoadedPeriod,SERIES_FIRSTDATE,first_date);
   int bars=Bars(InpLoadedSymbol,InpLoadedPeriod);
   Print("First date ",first_date," - ",bars," bars");
//---
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CheckLoadHistory(string symbol,ENUM_TIMEFRAMES period,datetime start_date)
  {
   datetime first_date=0;
   datetime times[100];
//--- 심볼 및 주기 점검
   if(symbol==NULL || symbol==""symbol=Symbol();
   if(period==PERIOD_CURRENT)     period=Period();
//--- Market Watch에서 심볼이 선택되었는지 확인
   if(!SymbolInfoInteger(symbol,SYMBOL_SELECT))
     {
      if(GetLastError()==ERR_MARKET_UNKNOWN_SYMBOLreturn(-1);
      SymbolSelect(symbol,true);
     }
//--- 데이터가 있는지 확인
   SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date);
   if(first_date>0 && first_date<=start_date) return(1);
//--- 지표인 경우 자체 데이터 로드를 요청하지 않음
   if(MQL5InfoInteger(MQL5_PROGRAM_TYPE)==PROGRAM_INDICATOR && Period()==period && Symbol()==symbol)
      return(-4);
//--- 두번째 시도
   if(SeriesInfoInteger(symbol,PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_date))
     {
      //--- 시계열을 빌드하기 위해 로드된 데이터가 있습니다.
      if(first_date>0)
        {
         //--- 시계열 강제 생성
         CopyTime(symbol,period,first_date+PeriodSeconds(period),1,times);
         //--- 날짜 점검
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date))
            if(first_date>0 && first_date<=start_date) return(2);
        }
     }
//--- 터미널 옵션에서 차트의 최대 막대
   int max_bars=TerminalInfoInteger(TERMINAL_MAXBARS);
//--- 심볼 내역 정보 로드
   datetime first_server_date=0;
   while(!SeriesInfoInteger(symbol,PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date) && !IsStopped())
      Sleep(5);
//--- 로드 시작 날짜 지정
   if(first_server_date>start_date) start_date=first_server_date;
   if(first_date>0 && first_date<first_server_date)
      Print("주의: 첫번째 서버 날짜",first_server_date," for ",symbol,
            " 첫 번째 시리즈 날짜와 일치하지 않음 ",first_date);
//--- 데이터를 차근차근 로드
   int fail_cnt=0;
   while(!IsStopped())
     {
      //--- 시계열 제작 대기
      while(!SeriesInfoInteger(symbol,period,SERIES_SYNCHRONIZED) && !IsStopped())
         Sleep(5);
      //--- 막대 제작 요청
      int bars=Bars(symbol,period);
      if(bars>0)
        {
         if(bars>=max_bars) return(-2);
         //--- 첫번째 날짜 요청
         if(SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE,first_date))
            if(first_date>0 && first_date<=start_date) return(0);
        }
      //--- 다음 파트의 복제 데이터 강제 로드
      int copied=CopyTime(symbol,period,bars,100,times);
      if(copied>0)
        {
         //--- 데이터 점검
         if(times[0]<=start_date)  return(0);
         if(bars+copied>=max_bars) return(-2);
         fail_cnt=0;
        }
      else
        {
         //--- 100회 이하의 시도 실패
         fail_cnt++;
         if(fail_cnt>=100) return(-5);
         Sleep(10);
        }
     }
//--- 정지됨
   return(-3);
  }
//+------------------------------------------------------------------+
//| 주기의 문자열 값을 반환                               |
//+------------------------------------------------------------------+
string GetPeriodName(ENUM_TIMEFRAMES period)
  {
   if(period==PERIOD_CURRENTperiod=Period();
//---
   switch(period)
     {
      case PERIOD_M1:  return("M1");
      case PERIOD_M2:  return("M2");
      case PERIOD_M3:  return("M3");
      case PERIOD_M4:  return("M4");
      case PERIOD_M5:  return("M5");
      case PERIOD_M6:  return("M6");
      case PERIOD_M10return("M10");
      case PERIOD_M12return("M12");
      case PERIOD_M15return("M15");
      case PERIOD_M20return("M20");
      case PERIOD_M30return("M30");
      case PERIOD_H1:  return("H1");
      case PERIOD_H2:  return("H2");
      case PERIOD_H3:  return("H3");
      case PERIOD_H4:  return("H4");
      case PERIOD_H6:  return("H6");
      case PERIOD_H8:  return("H8");
      case PERIOD_H12return("H12");
      case PERIOD_D1:  return("Daily");
      case PERIOD_W1:  return("Weekly");
      case PERIOD_MN1return("Monthly");
     }
//---
   return("알수없는 주기");
  }