
MQL5로 틱 인디케이터 만들기
개요
거래를 할 때는 가격 변동에 대한 세세한 정보를 최대한 많이 갖고 있는 게 좋겠죠. 틱 차트를 이용하면 됩니다. 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 변수는 정수형으로 파일 핸들 저장에 쓰이고, BidPosition과 AskPosition 변수는 문자열 내 매수호가 및 매도호가 저장, 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[]) {
// 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 변수는 파일 핸들 저장에 쓰이고, BidPosition과 AskPosition 변수는 문자열 내 매수호가 및 매도호가 저장, 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



