English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
MQL5로 틱 인디케이터 만들기

MQL5로 틱 인디케이터 만들기

MetaTrader 5지표 | 5 7월 2021, 10:09
182 0
Denis Zyatkevich
Denis Zyatkevich

개요

거래를 할 때는 가격 변동에 대한 세세한 정보를 최대한 많이 갖고 있는 게 좋겠죠. 틱 차트를 이용하면 됩니다. MQL5로 틱 차트를 만들어 볼게요.

이 글은 틱 가격 차트와 특정 개수의 틱을 이용해 캔들을 그리는 '틱 캔들' 차트 두 가지의 작성 방법을 다룹니다. 두 인디케이터 모두 수신된 가격 값을 정보로 만들어 터미널 재시작 시 사용 가능한 인디케이터 데이터(다른 프로그램에서도 이용 가능)를 만들죠.

틱 인디케이터 만들기

MQL5로 차트에 틱 데이터를 그리는 인디케이터를 작성해 봅시다. 그림 1.은 해당 인디케이터의 예시입니다.

그림 1. 틱 차트 예시

인디케이터는 매수 및 매도호가를 두 개의 선으로 나타냅니다. 각각의 기능은 인디케이터 옵션에서 해제할 수 있습니다.

인디케이터는 브로커로부터 텍스트 파일로 전달 받은 심볼의 가격을 다음 형식으로 저장합니다: 서버 시간, 매수호가 및 매도호가:

2010.03.26 19:43:02 1.33955 1.33968

파일명은 금융상품명과 동일하죠(예: EURUSD.txt). 이 파일들은 MT5_Folder\MQL5\Files에 저장됩니다. 인디케이터 옵션에서 추가 파일 디렉토리와 파일명 프리픽스를 설정할 수 있습니다(동일 심볼을 가진 차트에 여러 개의 인디케이터가 추가된 경우 유용).

인디케이터 작성을 위해 MetaTrader5를 실행한 후 F4키를 눌러 MetaQuotes 랭귀지 에디터를 엽니다. 이제 프로그램 코드를 작성해 볼까요.

인디케이터는 가격 차트 아래에 있는 별개의 창에 그려져야 한다는 사실을 명심하세요.

// indicator in a separate window
#property indicator_separate_window

매수호가 및 매도호가를 나타내는 두 개의 인디케이터 선을 그려야 하므로 두 가지 그래픽 플롯을 이용해야 하겠네요.

// two graphic plots are used: for Bid and Ask lines
#property indicator_plots 2

차트에 나타낼 데이터를 각각의 인디케이터 버퍼에 명시해 줍니다.

// two indicator's buffers
#property indicator_buffers 2

각각의 인디케이터 선에 드로잉 타입 DRAW_LINE(선)과 STYLE_SOLID(실선)를 적용하고 '매수호가'와 '매도호가'라는 라벨을 지정할게요.

// drawing type of a Bid line
#property indicator_type1 DRAW_LINE
// drawing color of a Bid line
#property indicator_color1 Red
// drawing style of a Bid line
#property indicator_style1 STYLE_SOLID
// text label of a Bid line
#property indicator_label1 "Bid"
// drawing type of an Ask line
#property indicator_type2 DRAW_LINE
// drawing color of an Ask line
#property indicator_color2 Blue
// drawing style of an Ask line
#property indicator_style2 STYLE_SOLID
// text label of an Ask line
#property indicator_label2 "Ask"

 이번에는 인디케이터 옵션에서 사용자가 변경 가능한 입력 변수를 정의합니다.

// the BidLineEnable indicates showing of a Bid line
input bool BidLineEnable=true; // Show Bid Line
// the AskLineEnable indicates showing of an Ask line
input bool AskLineEnable=true; // Show Ask Line
// the path_prefix defines a path and file name prefix
input string path_prefix=""; // FileName Prefix

BidLineEnable AskLineEnable 변수를 통해 인디케이터에서 해당 선을 표시하거나 감출 수 있습니다. path_prefix 변수를 이용해 파일명 앞에 오는 프리픽스를 설정할 수도 있고요. 이 변수를 이용하면 서브 디렉토리도 설정 가능합니다. 예를 들어, path_prefix를 'MyBroker/test_'로 설정하는 경우, 파일은 'MetaTrader5_Folder\MQL5\Files\MyBroker'에 저장되고, EURUSD 심볼의 파일명은 'test_EURUSD.txt'가 됩니다.

다시 전역 수준으로 돌아와서, 인디케이터 내 다양한 함수에 필요한 변수를 선언하겠습니다. 변수 값은 인디케이터 호출 중간 중간에 저장됩니다.

// the tick_stored variable is a number of served quotes
int ticks_stored;
// the BidBuffer[] and AskBuffer[] arrays - are indicator's buffers
double BidBuffer[],AskBuffer[];

tick_stored 변수는 사용 가능한 인용문의 수를 저장하는 데 사용됩니다. BidBuffer[]AskBuffer[]는 인디케이터 버퍼로 사용되는 동적 배열입니다. 차트에 매수호가와 매도호가로 나타나는 가격 데이터가 이 버퍼에 저장됩니다.

OnInit 함수는 BidBuffer[]AskBuffer[] 배열이 플로팅에 필요한 데이터를 포함하고 있음을 나타냅니다. 인디케이터 버퍼에서 0을 값으로 하는 데이터는 차트에 나타나면 안된다는 걸 꼭 기억하세요.

void OnInit()
  {
   // the BidBuffer[] is an indicator buffer
   SetIndexBuffer(0,BidBuffer,INDICATOR_DATA);
   // the AskBuffer[] is an indicator buffer
   SetIndexBuffer(1,AskBuffer,INDICATOR_DATA);
   // setting EMPTY_VALUE for a Bid line
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
   // setting EMPTY_VALUE for an Ask line
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);
  }

이제 OnCalculate 함수를 이용해 호출 시 전달되는 모든 매개 변수 리스트를 작성할 차례입니다: 

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])

변수를 선언해 보죠.

// the file_handle variable is a file handle
// the BidPosition and AskPosition - are positions of Bid and Ask prices in the string;
// the line_string_len is a length of a string, read from the file, i is a loop counter;
int file_handle,BidPosition,AskPosition,line_string_len,i;
// the last_price_bid is the last Bid quote
double last_price_bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
// the last_price_ask is the last Ask quote
double last_price_ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
// the filename is a name of a file, the file_buffer is a string, 
// used as a buffer for reading and writing of string data
string filename,file_buffer;

file_handle 변수는 정수형으로 파일 핸들 저장에 쓰이고, BidPositionAskPosition 변수는 문자열 내 매수호가 및 매도호가 저장, line_string_len는 파일에서 읽혀진 문자열 길이의 저장에 사용되며 i 변수는 루프 카운터로 사용될 겁니다. 마지막으로 수신된 매수 및 매도호가는 last_price_bid 및 last_price_ask 변수에 저장됩니다. filename 문자열 변수에는 파일명이 저장되며, file_buffer 문자열은 파일을 읽거나 파일에 쓰는데 사용됩니다.

파일명은 path_prefix 변수를 통해 생성되며, 금융상품명에 .txt 확장자가 붙는 식입니다. 더하기 연산자로 문자열을 연속적으로 사용하는 것보다 StringConcatenate 함수를 사용하는 것이 속도도 더 빠르고 메모리도 더 적게 사용됩니다.

// File name formation from the path_prefix variable, name
// of financial instrument and ".Txt" symbols
StringConcatenate(filename,path_prefix,Symbol(),".txt");

FileOpen 함수로 파일을 열겠습니다.

// Opening a file for reading and writing, codepage ANSI, shared reading mode
file_handle=FileOpen(filename,FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ);

파일 읽기 및 쓰기를 위해 FILE_READ 및 FILE_WRITE 플래그를 사용하며, ANSI 코드페이지의 사용(유니코드 기본 설정)을 알리는 FILE_ANSI, 그리고 다른 응용 프로그램 사용 시 공유 액세스를 통한 파일 읽기를 가능하게 하는 FILE_SHARE_READ 플래그가 사용됩니다.

인디케이터 첫 실행 시 아무 데이터도 나타나지 않으며 그렇지 않으면 차트의 기간이 변경된 것입니다.

 // At first execution of OnCalculate function, we are reading the quotes from a file
 if(prev_calculated==0)
  {
   // Reading the first line from the file and determine the length of a string
   line_string_len=StringLen(FileReadString(file_handle))+2;
   // if file is large (contains more quotes than rates_total/2)
   if(FileSize(file_handle)>(ulong)line_string_len*rates_total/2)
     {
      // Setting file pointer to read the latest rates_total/2 quotes
      FileSeek(file_handle,-line_string_len*rates_total/2,SEEK_END);
      // Moving file pointer to the beginning of the next line
      FileReadString(file_handle);
     }
   // if file size is small
   else
     {
      // Moving file pointer at the beginning of a file
      FileSeek(file_handle,0,SEEK_SET);
     }
   // Reset the counter of stored quotes
   ticks_stored=0;
   // Reading until the end of the file
   while(FileIsEnding(file_handle)==false)
    {
      // Reading a string from the file
      file_buffer=FileReadString(file_handle);
      // Processing of string if its length is larger than 6 characters
      if(StringLen(file_buffer)>6)
        {
         // Finding the start position of Bid price in the line
         BidPosition=StringFind(file_buffer," ",StringFind(file_buffer," ")+1)+1;
         // Finding the start position of Ask price in the line
         AskPosition=StringFind(file_buffer," ",BidPosition)+1;
         // If the Bid line should be plotted, adding this value to BidBuffer[] array
         if(BidLineEnable) 
         BidBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,BidPosition,AskPosition-BidPosition-1));
         // If the Ask line should be plotted, adding this value to AskBuffer[] array
         if(AskLineEnable) 
         AskBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,AskPosition));
         // Increasing the counter of stored quotes
         ticks_stored++;
        }
     }
  }

파일에서 불러올 인용문 수를 차트에 나타난 바의 개수의 절반으로 제한할게요. 파일에서 문자열을 읽은 후 길이를 구합니다. 줄 끝에 10번과 13번 코드('새줄 문자'와 '캐리지 리턴')를 사용한 글자 두 개가 있으니 줄의 길이를 2 만큼 증가시킵니다.

파일 내 다른 줄들의 평균 길이가 동일하다고 가정하겠습니다. 파일의 길이가 rates_total 변수를 2로 나눈 수 만큼의 줄보다 길다면(파일 내 인용문이 rates_total 변수를 2로 나눈 수보다 많다면), rates_total 변수를 2로 나눈 수 만큼의 인용문을 읽습니다. 다음으로 문자열 길이를 rates_total 변수를 2로 나눈 값과 동일한 거리(파일 끝 기준)에 파일 포인터를 설정하고, 파일에서 한 줄을 읽어 파일 포인터를 줄의 시작 지점에 맞게 정렬합니다.

if 연산자를 사용하여 두 값을 비교하면 두 가지 형식이 나옵니다. 파일의 길이는 ulong 형식, 우측의 표현식은 int 형식이죠. 따라서 우측의 표현식을 ulong 형식으로 타입 변환합니다.

파일 내 인용문 개수가 rates_total 변수를 2로 나눈 값보다 적다면, 파일 포인터를 파일 시작 지점으로 옮깁니다. 

인용문 카운터를 0으로 설정하고 파일의 처음부터 끝까지 위치한 모든 줄을 읽습니다. 문자열 처리는 날짜, 시간, 매도 및 매수호가와 각 문자 사이의 구분자를 나타내는 최소 길이인 문자 6자 이상을 포함하는 문자열에 대해서만 진행됩니다. 문자열에서 매도 및 매수호가 값을 추출한 후 해당 줄의 플로팅이 필요한지 확인한 후 인용문 카운터를 늘립니다.

이미 읽혀진 데이터의 경우 FileSeek 함수(새로운 데이터가 파일에 쓰임)를 이용하여 파일 포인터를 파일 끝으로 옮깁니다. StringConcatenate 함수를 이용해 문자열을 생성한 후 FileWrite 함수를 사용해 파일에 입력합니다. 새로운 매도 및 매수호가 값을 BidBuffer[]AskBuffer[] 배열에 입력하고, 해당 줄의 플로팅의 필요한 경우 인용문 카운터를 늘립니다.

  // If the data have been read before
else
  {
   // Moving file pointer at the end of the file
   FileSeek(file_handle,0,SEEK_END);
   // Forming a string, that should be written to the file
   StringConcatenate(file_buffer,TimeCurrent()," ",DoubleToString(last_price_bid,_Digits)," ",DoubleToString(last_price_ask,_Digits));
   // Writing a string to the file
   FileWrite(file_handle,file_buffer);
   // If the Bid line should be plotted, adding the last Bid price to the BidBuffer[] array
   if(BidLineEnable) BidBuffer[ticks_stored]=last_price_bid;
   // If the Ask line should be plotted, adding the last Ask price to the AskBuffer[] array
   if(AskLineEnable) AskBuffer[ticks_stored]=last_price_ask;
   // Increasing the quotes counter
   ticks_stored++;
  }

이쯤 되면 왜OnInit 함수 내부에 위치한 파일에서 데이터를 읽지 않는지 궁금해지죠? 그 이유는 BidBuffer[]AskBuffer[] 두 동적 배열에 대한 길이가 정의되지 않았으며, OnCalculate 함수의 호출 시기가 정해져 있기 때문입니다.

열었던 파일을 닫습니다.

// Closing the file
FileClose(file_handle);

파일 읽기 후 또는 BidBuffer[]AskBuffer[] 문자열에 추가한 후에도 인용문 카운터가 차트에 나타난 바의 개수와 같거나 큰 경우, 이전 인용문의 절반이 삭제되고 나머지가 그 자리를 대체하게 됩니다.

// If number of quotes is more or equal than number of bars in the chart
if(ticks_stored>=rates_total)
  {
   // Removing the first tick_stored/2 quotes and shifting remaining quotes
   for(i=ticks_stored/2;i<ticks_stored;i++)
     {
      // If the Bid line should be plotted, shifting the values of BidBuffer[] array on tick_stored/2
      if(BidLineEnable) BidBuffer[i-ticks_stored/2]=BidBuffer[i];
      // If the Ask line should be plotted, shifting the values of AskBuffer[] array on tick_stored/2
      if(AskLineEnable) AskBuffer[i-ticks_stored/2]=AskBuffer[i];
     }
   // Changing the value of a counter
   ticks_stored-=ticks_stored/2;
  }

인디케이터 버퍼의 BidBuffer[]AskBuffer[] 배열은 시계열이 아니므로 직전 엘리먼트는 ticks_stored 변수에서 1을 뺀 값과 같은 개수의 인덱스를, 직전 차트 바는 rates_total 변수에서 1을 뺀 값과 같은 개수의 인덱스를 갖습니다. 동일한 수준에서 두 배열을 결합하기 위해 PlotIndexSetInteger 함수를 이용해 인디케이터 선을 이동하겠습니다.

// Shifting the Bid line to align with the price chart
PlotIndexSetInteger(0,PLOT_SHIFT,rates_total-ticks_stored);
// Shifting the Ask line to align with the price chart
PlotIndexSetInteger(1,PLOT_SHIFT,rates_total-ticks_stored);

수신된 가격 값은 rates_total 변수에서 1을 뺀 값과 같은 개수의 인덱스를 갖는 BidBuffer[]AskBuffer[] 배열에 저장(해당 선이 나타날 경우)되며 인디케이터 창의 상위 왼쪽 코너에 나타납니다.

// If the Bid line should be plotted, placing the value to the last element 
// of BidBuffer [] array to show the last Bid price in the indicator's window  
if(BidLineEnable) BidBuffer[rates_total-1]=last_price_bid;
// If the Ask line should be plotted, placing the value to the last element 
// of AskBuffer [] array to show the last Ask price in the indicator's window
if(AskLineEnable) AskBuffer[rates_total-1]=last_price_ask;

rates_total 변수(0이 아닌 모든 수)의 반환으로 OnCalculate 함수의 실행이 완료되었으며 }로 코드 작성을 마무리합니다.

// Return from OnCalculate(), return a value, different from zero   
return(rates_total);
}

이렇게 틱 인디케이터가 완성되었습니다. 전체 소스 코드는 페이지 하단에 위치한 링크를 통해 다운로드 가능합니다.

'틱 캔들' 인디케이터 만들기

이번에는 흔히들 '틱 캔들'이라고 부르는 걸 그리는 인디케이터를 작성해 보죠. 각각의 캔들이 특정한 기간을 나타내는 전통적인 캔들 차트와는 달리, '틱 캔들' 차트는 각각의 캔들이 브로커로부터 전달된 미리 정의된 개수의 틱을 갖고 있는 구조(이퀴볼륨 캔들)입니다. 그림 2와 같은 모습입니다.


그림 2. '틱 캔들' 인디케이터

위에서 다룬 틱 인디케이터와 마찬가지로 '틱 캔들' 인디케이터 또한 파일로 전달되는 모든 인용문을 쓰게 됩니다. 데이터 형식 및 파일 저장 위치도 동일합니다. 파일 경로, 파일명 프리픽스, 캔들별 틱의 개수 및 호가 형식(매수 또는 매도)은 인디케이터 옵션에서 설정 가능합니다.

인디케이터 작성을 위해 MetaTrader5를 실행한 후 F4키를 눌러 MetaQuotes 랭귀지 에디터를 엽니다.

기억하세요. 인디케이터는 별개의 창에 그려져야 합니다.

// Indicator is plotted in a separate window
#property indicator_separate_window

이번 인디케이터는 한 가지 그래픽 플롯만 이용합니다. 컬러 캔들이죠.

// One graphic plot is used, color candles
#property indicator_plots 1

컬러 캔들을 보여주기 위해, 그리고 각 캔들의 가격 데이터 값(시가, 고가, 저가 및 종가)을 저장하기 위해 하나씩의 버퍼가 필요합니다. 또한 캔들의 컬러 인덱스를 저장하기 위해 버퍼가 하나 추가됩니다.

// We need 4 buffers for OHLC prices and one - for the index of color
#property indicator_buffers 5

컬러 캔들을 그리는 드로잉 타입인 DRAW_COLOR_CANDLES을 실행해 보도록 하죠.

// Specifying the drawing type - color candles
#property indicator_type1 DRAW_COLOR_CANDLES

캔들에 사용될 색깔을 정의할게요.

// Specifying the colors for the candles
#property indicator_color1 Gray,Red,Green

매도 또는 매수호가 중 한 값을 포함하는 열거형 price_types 변수를 만들고요.

/ / Declaration of the enumeration
enum price_types
  (
   Bid,
   Ask
  )

인디케이터 옵션에서 사용자가 변경 가능한 입력 변수를 정의합니다.

// The ticks_in_candle input variable specifies the number of ticks,
// corresponding to one candle
input int ticks_in_candle=16; //Tick Count in Candles
// The applied_price input variable of price_types type indicates 
// the type of the data, that is used in the indicator: Bid or Ask prices.
input price_types applied_price=0; // Price
// The path_prefix input variable specifies the path and prefix to the file name
input string path_prefix=""; // FileName Prefix

ticks_in_candle 변수는 하나의 캔들을 이루는 틱의 개수를 규정합니다. applied_price 변수는 캔들 형성에 사용된 가격의 형식이 매도호가인지 매수호가인지를 나타내죠. 과거 틱 데이터 관련 디렉토리 및 파일명 프리픽스는 path_prefix 변수로 설정 가능합니다.

인디케이터 호출 사이에 저장되는 변수 값은 전역 수준에서 선언됩니다.

// The ticks_stored variable contains the number of stored quotes
int ticks_stored;
// The TicksBuffer [] array is used to store the incoming prices
// The OpenBuffer [], HighBuffer [], LowBuffer [] and CloseBuffer [] arrays
// are used to store the OHLC prices of the candles
// The ColorIndexBuffer [] array is used to store the index of color candles
double TicksBuffer[],OpenBuffer[],HighBuffer[],LowBuffer[],CloseBuffer[],ColorIndexBuffer[];

ticks_stored 변수는 사용 가능한 인용문의 수를 저장하는 데 사용됩니다. TicksBuffer[] 배열은 수신된 인용문의 저장에 쓰이며, OpenBuffer[], HighBuffer[], LowBuffer[]CloseBuffer[] 배열은 차트에 그려질 캔들의 가격(시가, 고가, 저가 및 종가)의 저장에 사용됩니다. ColorIndexBuffer[] 배열은 캔들의 컬러 인덱스 저장에 사용되고요.

OnInit 함수는 OpenBuffer[], HighBuffer[], LowBuffer[]CloseBuffer[] 배열이 인디케이터 버퍼로 사용되었음을 나타내고, ColorIndexBuffer[] 배열은 캔들의 컬러 인덱스를 포함하며, TicksBuffer[] 배열은 중간 연산에 사용됩니다.

void OnInit()
  {
   // The OpenBuffer[] array is an indicator buffer
   SetIndexBuffer(0,OpenBuffer,INDICATOR_DATA);
   // The HighBuffer[] array is an indicator buffer
   SetIndexBuffer(1,HighBuffer,INDICATOR_DATA);
   // The LowBuffer[] array is an indicator buffer
   SetIndexBuffer(2,LowBuffer,INDICATOR_DATA);
   // The CloseBuffer[] array is an indicator buffer
   SetIndexBuffer(3,CloseBuffer,INDICATOR_DATA);
   // The ColorIndexBuffer[] array is the buffer of the color index
   SetIndexBuffer(4,ColorIndexBuffer,INDICATOR_COLOR_INDEX);
   // The TicksBuffer[] array is used for intermediate calculations
   SetIndexBuffer(5,TicksBuffer,INDICATOR_CALCULATIONS);

이번에는 OpenBuffer[], HighBuffer[], LowBuffer[], CloseBuffer[]ColorIndexBuffer[] 배열을 시계열(가장 최근 데이터가 0)로 규정합니다.

   // The indexation of OpenBuffer[] array as timeseries
   ArraySetAsSeries(OpenBuffer,true);
   // The indexation of HighBuffer[] array as timeseries
   ArraySetAsSeries(HighBuffer,true);
   // The indexation of LowBuffer[] array as timeseries
   ArraySetAsSeries(LowBuffer,true);
   // The indexation of CloseBuffer[] array as timeseries
   ArraySetAsSeries(CloseBuffer,true);
   // The indexation of the ColorIndexBuffer [] array as timeseries
   ArraySetAsSeries(ColorIndexBuffer,true);

인디케이터 버퍼에서 0을 값으로 갖는 데이터는 차트에 나타나지 않을 겁니다.

   // The null values of Open prices (0th graphic plot) should not be plotted
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
   // The null values of High prices (1st graphic plot) should not be plotted
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0);
   // The null values of Low prices (2nd graphic plot) should not be plotted
   PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,0);
   // The null values of Close prices (3rd graphic plot) should not be plotted
   PlotIndexSetDouble(3,PLOT_EMPTY_VALUE,0);

작성이 완료된 OnInit 함수를 }로 닫습니다.

OnCalculate 함수를 작성할 차례입니다. 함수로 전달된 모든 매개 변수를 설정하겠습니다.

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {

OnInit 함수에서 사용될 변수를 선언합니다.

// the file_handle variable is a file handle
// the BidPosition and AskPosition - are positions of Bid and Ask prices in the string;
// the line_string_len is a length of a string, read from the file, 
// CandleNumber - number of candle, for which the prices OHLC are determined,
// i - loop counter;
int file_handle,BidPosition,AskPosition,line_string_len,CandleNumber,i;
// The last_price_bid variable is the recent received Bid price
double last_price_bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
// The last_price_ask variable is the recent received Ask price
double last_price_ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
// the filename is a name of a file, the file_buffer is a string, 
// used as a buffer for reading and writing of string data
string filename,file_buffer;

정수형인 file_handle 변수는 파일 핸들 저장에 쓰이고, BidPositionAskPosition 변수는 문자열 내 매수호가 및 매도호가 저장, line_string_len 변수는 파일에서 읽혀진 문자열 길이 저장에 사용되며 CandleNumber 변수는 계산된 캔들의 인덱스로, i 변수는 루프 카운터로 사용됩니다.

마지막으로 수신된 매수 및 매도호가는 last_price_bid 와 last_price_ask 두 이중 배열 변수에 저장됩니다. filename 변수는 파일명 저장에 사용되는 문자열이며, file_buffer는 파일 연산에 사용되는 문자열입니다.

TicksBuffer[] 배열의 크기는 인디케이터 버퍼인 OpenBuffer[], HighBuffer[], LowBuffer[], CloseBuffer[]  또는 ColorIndexBuffer[]처럼 자동으로 설정되지 않기 때문에 TicksBuffer[] 배열의 크기를 OpenBuffer[], HighBuffer[], LowBuffer[], CloseBuffer[]ColorIndexBuffer[] 배열과 동일하게 설정하도록 하겠습니다.

// Setting the size of TicksBuffer[] array
ArrayResize(TicksBuffer,ArraySize(CloseBuffer));

path_prefix 변수로 파일명을 정하는데, 금융상품명에 .txt 확장자가 붙는 형식입니다.

// File name formation from the path_prefix variable, name
// of financial instrument and ".Txt" symbols
StringConcatenate(filename,path_prefix,Symbol(),".txt");

이제 이전 인디케이터에 사용된 매개 변수를 이용해 파일을 열어보죠.

// Opening a file for reading and writing, codepage ANSI, shared reading mode
file_handle=FileOpen(filename,FILE_READ|FILE_WRITE|FILE_ANSI|FILE_SHARE_READ);

OnCalculate 함수가 처음 호출되었을 때 TicksBuffer[] 배열에 데이터가 없는 경우 파일에서 읽어옵니다.

if(prev_calculated==0)
  {
   // Reading the first line from the file and determine the length of a string
   line_string_len=StringLen(FileReadString(file_handle))+2;
   // if file is large (contains more quotes than rates_total/2)
   if(FileSize(file_handle)>(ulong)line_string_len*rates_total/2)
     {
      // Setting file pointer to read the latest rates_total/2 quotes
      FileSeek(file_handle,-line_string_len*rates_total/2,SEEK_END);
      // Moving file pointer to the beginning of the next line
      FileReadString(file_handle);
     }
   // if file size is small
   else
     {
      // Moving file pointer at the beginning of a file
      FileSeek(file_handle,0,SEEK_SET);
     }
   // Reset the counter of stored quotes
   ticks_stored=0;
   // Reading until the end of the file
   while(FileIsEnding(file_handle)==false)
     {
      // Reading a string from thefile
      file_buffer=FileReadString(file_handle);
      // Processing of string if its length is larger than 6 characters
      if(StringLen(file_buffer)>6)
        {
         // Finding the start position of Bid price in the line
         BidPosition=StringFind(file_buffer," ",StringFind(file_buffer," ")+1)+1;
          //Finding the start position of Ask price in the line
         AskPosition=StringFind(file_buffer," ",BidPosition)+1;
         // If the Bid prices are used, adding the Bid price to TicksBuffer[] array
         if(applied_price==0)
         TicksBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,BidPosition,AskPosition-BidPosition-1));
         // If the Ask prices are used, adding the Ask price to TicksBuffer[] array
         if(applied_price==1)
         TicksBuffer[ticks_stored]=StringToDouble(StringSubstr(file_buffer,AskPosition));
         // Increasing the counter of stored quotes
         ticks_stored++;
        }
     }
  }

인용문에 관련한 내용은 이전 인디케이터와 동일하니 위의 내용을 참조하세요.

만약 인용문이 TicksBuffer[] 배열에 이미 읽힌 경우, 파일에 새로운 가격 값을 입력하고 새로운 가격을 TicksBuffer[] 배열에 저장한 뒤 인용문 카운터를 늘립니다.

// If the data have been read before
else
  {
   // Moving file pointer at the end of the file
   FileSeek(file_handle,0,SEEK_END);
   // Forming a string, that should be written to the file
   StringConcatenate(file_buffer,TimeCurrent()," ",DoubleToString(last_price_bid,_Digits)," ",DoubleToString(last_price_ask,_Digits));
   // Writing a string to the file
   FileWrite(file_handle,file_buffer);
   // If the Bid prices are used, adding the last Bid price to TicksBuffer[] array
   if(applied_price==0) TicksBuffer[ticks_stored]=last_price_bid;
   // If the Ask prices are used, adding the last Ask price to TicksBuffer[] array
   if(applied_price==1) TicksBuffer[ticks_stored]=last_price_ask;
   // Increasing the quotes counter
   ticks_stored++;
  }

파일 닫기

// Closing the file
FileClose(file_handle);

저장된 인용문의 수가 차트에 나타난 바의 수에 도달하거나 넘어설 경우, 가장 오래된 데이터의 절반을 삭제하고 나머지 데이터로 대체합니다.

// If number of quotes is more or equal than number of bars in the chart
if(ticks_stored>=rates_total)
  {
   // Removing the first tick_stored/2 quotes and shifting remaining quotes
   for(i=ticks_stored/2;i<ticks_stored;i++)
     {
      // Shifting the data to the beginning in the TicksBuffer[] array on tick_stored/2
      TicksBuffer[i-ticks_stored/2]=TicksBuffer[i];
     }
   // Changing the quotes counter
   ticks_stored-=ticks_stored/2;
  }

이제 각 캔들에 해당하는 OHLC 값을 구해 해당하는 인디케이터 버퍼에 저장하도록 하죠.

   // We assign the CandleNumber with a number of invalid candle
   CandleNumber=-1;
   // Search for all the price data available for candle formation
   for(i=0;i<ticks_stored;i++)
     {
      // If this candle is forming already
      if(CandleNumber==(int)(MathFloor((ticks_stored-1)/ticks_in_candle)-MathFloor(i/ticks_in_candle)))
        {
         // The current quote is still closing price of the current candle
         CloseBuffer[CandleNumber]=TicksBuffer[i];
         // If the current price is greater than the highest price of the current candle,
          // it will be a new highest price of the candle
         if(TicksBuffer[i]>HighBuffer[CandleNumber]) HighBuffer[CandleNumber]=TicksBuffer[i];
         // If the current price is lower than the lowest price of the current candle, 
          // it will be a new lowest price of the candle
         if(TicksBuffer[i]<LowBuffer[CandleNumber]) LowBuffer[CandleNumber]=TicksBuffer[i];
         // If the candle is bullish, it will have a color with index 2 (green)
         if(CloseBuffer[CandleNumber]>OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=2;
         // If the candle is bearish, it will have a color with index 1 (red)
         if(CloseBuffer[CandleNumber]<OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=1;
         // If the opening and closing prices are equal, then the candle will have a color with index 0 (grey)
         if(CloseBuffer[CandleNumber]==OpenBuffer[CandleNumber]) ColorIndexBuffer[CandleNumber]=0;
        }
      // If this candle hasn't benn calculated yet
      else
        {
         // Let's determine the index of a candle
         CandleNumber=(int)(MathFloor((ticks_stored-1)/ticks_in_candle)-MathFloor(i/ticks_in_candle));
         // The current quote will be the opening price of a candle
         OpenBuffer[CandleNumber]=TicksBuffer[i];
         // The current quote will be the highest price of a candle
         HighBuffer[CandleNumber]=TicksBuffer[i];
         // The current quote will be the lowest price of a candle
         LowBuffer[CandleNumber]=TicksBuffer[i];
         // The current quote will be the closing price of a candle
         CloseBuffer[CandleNumber]=TicksBuffer[i];
         // The candle will have a color with index 0 (gray)
         ColorIndexBuffer[CandleNumber]=0;
        }
     }

0이 아닌 값을 반환하여 OnCalculate 함수의 실행이 완료되었으며 이는 TicksBuffer[] 배열에 이미 데이터가 저장되어 있고 다음 함수 호출 시 추가 읽기가 불필요함을 의미합니다. }로 함수를 마무합니다.

 // Return from OnCalculate(), return a value, different from zero   
 return(rates_total);
}

인디케이터의 전체 소스 코드는 페이지 하단 링크에서 다운로드 가능합니다.

결론

이번에는 틱 차트 인디케이터와 '틱 캔들' 인디케이터를 만드는 방법을 살펴보았습니다.

MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/60

파일 첨부됨 |
tickindicator.mq5 (8.28 KB)
MetaTrader5가 주는 새로운 기회 MetaTrader5가 주는 새로운 기회
전 세계 투자자들에게 사랑 받은 MetaTrader4는 그 자체로 정말 완벽해 보였죠. 빠른 처리 속도, 안정성, 다양한 인디케이터 작성 방법, 엑스퍼트 어드바이저, 거래 시스템, 그리고 선택 가능한 브로커의 수까지, 다른 터미널과는 비교가 불가능할 정도였으니까요. 하지만 시간이 흘러 이제 우리는 MetaTrader4와 MetaTrader5 사이의 선택의 길에 놓여 있는데요. 이 글에서는 MetaTrader5의 주요 차별점에 대해 이야기하도록 하겠습니다.
MQL5의 드로잉 스타일 MQL5의 드로잉 스타일
MQL4에서는 6가지 드로잉 스타일이, MQL5에서는 18가지 드로잉 스타일이 지원됩니다. 그러니까 MQL5의 드로잉 스타일을 알아 보면 되겠죠? 이번 글에서는 MQL5에서 지원되는 드로잉 스타일에 대해 상세히 알아 보겠습니다. 그리고 인디케이터를 생성해서 드로잉 스타일도 설명하고 플롯도 개선해 보도록 할게요.
초보자를 위한 MQL5 Expert Advisor 코드 작성 가이드 초보자를 위한 MQL5 Expert Advisor 코드 작성 가이드
MQL5의 Expert Advisors 프로그래밍은 간단하며 쉽게 배울 수 있습니다. 이 단계별 가이드를 통해 개발된 거래 전략에 따라 간단한 Expert Advisor를 작성할 때 필요한 기본 단계를 확인할 수 있습니다. Expert Advisor의 구조, 내장 기술 인디케이터 및 거래 기능의 사용, 디버그 모드의 세부 사항 및 Strategy Tester의 사용 등이 소개되어 있습니다.
MQL5에서 인디케이터를 호출하는 방법 MQL5에서 인디케이터를 호출하는 방법
새로운 버전의 MQL 프로그래밍 언어를 사용할 수 있게 됨에 따라 지표 처리 방식이 변경되었을 뿐만 아니라 지표를 만드는 새로운 방법도 있습니다. 또한 인디케이터 버퍼로 작업 할 수 있는 추가적인 유연성이 있습니다. 이제 원하는 인덱싱 방향을 지정하고 원하는 만큼의 인디케이터 값을 얻을 수 있습니다. 이 문서에서는 인디케이터를 호출하고 인디케이터의 버퍼에서 데이터를 검색하는 기본 방법을 설명합니다.