스펙트럼 분석기 구축
소개
이 글은 독자들이 MQL5 언어의 그래픽 객체를 사용하는 가능한 변형에 대해 알게 하기 위한 것입니다. 그래픽 개체를 사용하여 간단한 스펙트럼 분석기의 제어 패널을 구현하는 표시기를 분석합니다.
기사에 3개의 파일이 첨부되어 있습니다.
- SpecAnalyzer.mq5 – 이 기사에서 설명하는 지표.
- SpecAnalyzer.mqh – SpecAnalyzer.mq5의 포함 파일입니다.
- SAInpData.mq5 – 외부 데이터에 대한 액세스 구성에 사용되는 지표입니다.
지표 SpecAnalyzer를 정상적으로 로드하려면 세 파일을 모두 클라이언트 터미널의 \MQL5\Indicators 폴더에 배치해야 합니다. 그런 다음 SpecAnalyzer.mq5 및 SAInpData.mq5 파일을 컴파일해야 합니다. 표시기는 기본 차트 창에 로드하기 위한 것입니다. 표시기가 로드되면 이 창의 표시 매개변수가 변경되고 표시기가 제거되면 창에서 모든 그래픽 개체가 삭제됩니다. 그렇기 때문에 터미널의 기존 창 표시 모드가 실수로 변경되는 것을 방지하기 위해 별도의 특수 창에 표시기를 로드해야 합니다.
기사에 표시기의 전체 소스 코드가 포함되어 있지 않다는 사실을 고려할 때 기사를 읽는 동안 첨부 파일의 코드를 열어 두는 것이 좋습니다.
기사에 설명된 지표는 기성 애플리케이션인 척하지 않습니다. 언어의 그래픽 개체를 사용하는 예일 뿐입니다. 관심 있는 사람은 기사에 표시된 코드를 자체적으로 업그레이드하고 최적화할 수 있습니다.
좌표
그래픽 객체를 그릴 때 좌표를 지정하기 위해 MQL5에서 두 가지 방법을 사용할 수 있습니다. 일부 객체의 경우 좌표는 창의 지정된 지점으로부터의 픽셀 수로 지정됩니다. 다른 사람들의 경우 좌표는 창에 표시된 차트의 가격 및 시간 값으로 지정됩니다.
예를 들어 "레이블" 또는 "버튼"과 같은 개체를 차트에 배치하려면 해당 좌표를 차트 창의 모서리 중 하나로부터의 픽셀에서의 거리로써 구체화 해야 합니다. 이러한 방식으로 개체를 지정하면 현재 창의 속성과 여기에 표시된 차트의 크기에 관계없이 개체의 포지션이 유지됩니다. 창 크기가 변경되더라도 이러한 개체는 포지션을 유지하고 서로 바인딩됩니다. 마우스 왼쪽 버튼을 사용하여 창에서 차트를 이동하면 해당 개체는 창의 선택된 앵커 포인트에 상대적으로 포지션을 유지합니다.
다른 개체 그룹은 창 좌표 대신 창의 차트에 바인딩하는 것을 의미합니다. 이러한 개체는 "추세선", "사각형" 등입니다. 이러한 개체를 생성하여 배치할 때 좌표는 창에 표시되는 차트의 시간 및 가격 값으로 지정됩니다. 이 좌표 지정 모드를 사용하면 차트 배율이 변경되거나 스크롤 될 때 개체가 차트 창에 대해 상대적으로 포지션이 변경되고 서로 간에 포지션이 변경됩니다. 차트에 새 막대가 표시되면 시간 축이 시간 프레임 크기에서 왼쪽으로 이동하므로 개체의 위치도 변경됩니다.
SpecAnalyzer 표시기에서 두 가지 유형의 그래픽 개체는 스펙트럼 분석기의 제어판을 만드는 데 동시에 사용됩니다. 차트에 바인딩된 개체가 창에 상대적으로 이동하지 않도록 차트의 세로 축을 따라 표시되는 고정 모드와 차트의 가로 눈금을 따라 표시할 수 있는 최소 눈금에 해당하는 모드를 설정했습니다. 또한 수직 스케일의 최소값은 0.0으로 설정됩니다. 수평 축의 경우 오른쪽 가장자리에서 제로 막대를 이동하지 않고 차트의 오른쪽 가장자리로 자동 스크롤하지 않고 모드를 설정합니다. 따라서 차트의 0점과 0.0 값과 일치하는 좌표점은 우측 하단 모서리에 나타나며 고정된 축척으로 "Trend Line" 및 "사각형"과 같은 개체의 기준점으로 사용할 수 있습니다. 이때 "Label" 또는 "Button"과 같은 개체의 앵커 포인트와 동일한 오른쪽 하단 모서리를 설정하면 둘 다 객체의 유형은 확실히 서로 바인딩됩니다.
모든 필요한 차트 속성은 SpecAnalyzer.mqh 파일의 SetOwnProperty() 함수에서 설정됩니다.
void GRaphChart::SetOwnProperty(void) { ChartSetInteger(m_chart_id,CHART_FOREGROUND,1); ChartSetInteger(m_chart_id,CHART_SHIFT,0); ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,0); ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,1); ChartSetInteger(m_chart_id,CHART_SCALE,0); ChartSetInteger(m_chart_id,CHART_SCALEFIX_11,1); ChartSetInteger(m_chart_id,CHART_SHOW_OHLC,0); ChartSetInteger(m_chart_id,CHART_SHOW_BID_LINE,0); ChartSetInteger(m_chart_id,CHART_SHOW_ASK_LINE,0); ChartSetInteger(m_chart_id,CHART_SHOW_LAST_LINE,0); ChartSetInteger(m_chart_id,CHART_SHOW_PERIOD_SEP,0); ChartSetInteger(m_chart_id,CHART_SHOW_GRID,0); ChartSetInteger(m_chart_id,CHART_SHOW_VOLUMES,CHART_VOLUME_HIDE); ChartSetInteger(m_chart_id,CHART_SHOW_OBJECT_DESCR,0); ChartSetInteger(m_chart_id,CHART_SHOW_TRADE_LEVELS,0); ChartSetInteger(m_chart_id,CHART_COLOR_BACKGROUND,Black); ChartSetInteger(m_chart_id,CHART_COLOR_FOREGROUND,Black); ChartSetDouble(m_chart_id,CHART_FIXED_MIN,0.0); }
그래픽 개체의 바인딩을 제공하기 위해 차트의 필수 속성을 설정하는 것 외에도 이 기능은 색상을 할당하고 차트의 일부 요소 표시를 제한할 수 있는 속성을 설정합니다.
표시기 SpecAnalyzer는 작업하는 동안 차트 속성을 변경하므로 표시기 로드 시 차트의 이전 설정을 저장하고 표시기 언로드 시 설정 복원을 제공해야 합니다. MQL5의 표준 라이브러리에는 CChart 클래스의 Save() 및 Load()를 위한 특수 가상 함수가 포함되어 있습니다. 이러한 함수는 CChart 클래스의 개체 속성을 파일에 저장하고 생성된 파일에서 해당 속성을 복원하기 위한 것입니다. 차트의 속성을 저장할 때 저장된 속성 집합을 변경하고 파일 작업 사용을 피하기 위해 CChart의 가상 함수 Save() 및 Load() 클래스는 새 클래스 GRAphChart를 만들 때 재정의됩니다(SpecAnalyzer.mqh 파일 참조).
class GRaphChart : public CChart { protected: struct ChartPropertyes { double shift_size; double fixed_max; double fixed_min; double points_per_bar; long mode; bool foreground; bool shift; bool autoscroll; long scale; bool scalefix; bool scalefix_11; bool scale_pt_per_bar; bool show_ohls; bool show_bid_line; bool show_ask_line; bool show_last_line; bool show_period_sep; bool show_grid; long show_volumes; bool show_object_descr; bool show_trade_levels; long color_background; long color_foreground; long color_grid; long color_volume; long color_chart_up; long color_chart_down; long color_chart_line; long color_candle_bull; long color_candle_bear; long color_bid; long color_ask; long color_last; long color_stop_level; string ch_comment; }; ChartPropertyes ChProp; public: GRaphChart(); ~GRaphChart(); void SetOwnProperty(); virtual void Save(); virtual void Load(); };
GRAphChart의 기본 클래스는 MQL5의 표준 라이브러리의 CChart입니다. GRAphChart 클래스에는 기본 클래스에서 구현된 파일 대신 메모리에 차트 속성을 저장하기 위한 ChartPropertyes 구조 및 ChProp 객체 생성에 대한 설명이 포함되어 있습니다. Save() 함수는 차트의 현재 속성에 따라 데이터로 ChProp 구조를 채우고, Load() 함수는 이전에 저장한 속성을 복원합니다.
Save() 함수는 GRAphChart 클래스의 생성자에서 호출되고 Load() 함수는 소멸자에서 호출됩니다. 그렇기 때문에 GRAphChart 클래스의 객체를 생성하고 삭제할 때 차트의 이전 상태에 대한 저장 및 복원이 자동으로 수행됩니다. 위에서 언급한 SetOwnProperty()는 클래스 생성자에서도 호출됩니다.
//---------------------------------------------------- Constructor GRaphChart -- GRaphChart::GRaphChart() { m_chart_id=ChartID(); Save(); // Keep a original chart settings SetOwnProperty(); // Set own chart settings } //----------------------------------------------------- Destructor GRaphChart -- GRaphChart::~GRaphChart() { Load(); // Restore a previous chart settings m_chart_id=-1; }
간단한 예를 들어 GRAphChart 클래스의 사용을 보여줍시다. 그렇게 하려면 MetaEditor에서 새 사용자 지정 표시기를 만들고 이름을 Test로 지정합니다. 지시자의 코드에 헤더 파일 SpecAnalyzer.mqh를 포함하고 두 줄을 추가하여 GRAphChart 클래스의 객체를 생성합니다.
//+------------------------------------------------------------------+ //| Test.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_chart_window #include "SpecAnalyzer.mqh" // <----- Including header file GRaphChart MainChart; // <----- Creating object of the class GRaphChart //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
위 코드를 성공적으로 컴파일하려면 SpecAnalyzer.mqh 파일이 클라이언트 터미널의 \MQL5\Indicators 폴더에 있어야 합니다.
클라이언트 터미널에서 차트를 만들고 테스트 예제를 로드하려고 하면 차트 속성이 변경되고 그 안에 그래픽 개체를 표시할 준비가 된 빈 창만 표시됩니다. 차트에서 테스트 지표를 제거하면 새 틱이 도착하면 초기 모양이 복원됩니다.
지표 SpecAnalyzer로 돌아가 보겠습니다. 지표의 시작 부분(SpecAnalyzer.mq5 파일 참조)에서 GRAphChart 클래스의 MainChart 객체 생성이 수행되어 지표를 로드하고 차트 속성을 저장합니다.
button. button. button.
GRaphChart MainChart; // Create MainChart object
button. button. button.
표시기를 언로드 할 때 MainChart 개체는 자동으로 종료되며 차트의 초기 속성이 클래스 소멸자에 복원됩니다.
제어판
표시기 SpecAnalyzer의 제어판 모양은 창에 배치된 그래픽 개체에 의해 결정됩니다. AllGrObject 클래스는 생성 및 상호 작용에 필요한 모든 기능을 통합합니다. SpecAnalyzer.mqh 파일을 참조하십시오.
class AllGrObject : public CChart { protected: long m_chart_id; // chart identifier public: AllGrObject(); ~AllGrObject(); void AddLabel(string name,int fsize,string font, color col,int x,int y,string text=""); void AddButton(string name,int xsize,int ysize,color bgcol, int fsize,string font,color col,int x,int y, string text="",int state=0); void AddEdit(string name,int xsize,int ysize,color bgcol,int fsize, string font,color col,int x,int y,string text=""); void AddTrendLine(string name,color col,int style=0,int width=1); void AddArrowline(string name,color col,int style=0,int width=1); void AddRectangle(string name,color col,int style=0,int width=1); void MoveGrObject(string name,int x1,int y1,int x2,int y2); void SetButtonProp(string name,bool state,color col); long GetRowNumber(string name); void LabelTxt(string name,string str); };
이름이 Add로 시작하는 클래스의 기능은 그래픽 개체를 생성하기 위한 것입니다. 예를 들어, AddButton()은 "Button" 객체를 생성합니다.
애플리케이션에서 모든 그래픽 개체의 좌표는 차트의 오른쪽 하단 모서리로부터의 거리(픽셀 단위)로 설정됩니다. "Trend Line", "Arrowed Line" 및 "Rectangle" 개체의 경우 이러한 좌표를 시간 및 가격 값으로 변환해야 합니다. 이러한 변환은 객체에 좌표를 할당하기 전에 MoveGrObject() 함수에서 수행됩니다. 하나의 수평 픽셀은 하나의 막대에 해당하고 하나의 수직 픽셀은 하나의 점에 해당합니다.
void AllGrObject::MoveGrObject(string name,int x1,int y1,int x2,int y2) { datetime t1[1],t2[1]; long typ; typ=ObjectGetInteger(m_chart_id,name,OBJPROP_TYPE); if(typ==OBJ_TREND||typ==OBJ_ARROWED_LINE||typ==OBJ_RECTANGLE) { CopyTime(_Symbol,_Period,x1,1,t1); CopyTime(_Symbol,_Period,x2,1,t2); ObjectSetInteger(m_chart_id,name,OBJPROP_TIME,0,t1[0]); ObjectSetDouble(m_chart_id,name,OBJPROP_PRICE,0,_Point*y1); ObjectSetInteger(m_chart_id,name,OBJPROP_TIME,1,t2[0]); ObjectSetDouble(m_chart_id,name,OBJPROP_PRICE,1,_Point*y2); } }
모든 그래픽 개체는 표시기에서 한 번만 생성되며 표시기의 OnInit() 함수에서 gr_object_create()를 호출할 때 수행됩니다. SpecAnalyzer.mq5 파일 참조. "Trend Line", "Arrowed Line", "Rectangle"을 제외한 모든 오브젝트는 좌표가 바로 설정됩니다. "Trend Line", "Arrowed Line" 및 "Rectangle" 좌표는 위에서 언급한 MoveGrObject() 함수는 주소 지정 모드를 변환합니다.
void gr_object_coordinate() { GObj.MoveGrObject("Trdline1",48,150,48,360); GObj.MoveGrObject("Trdline2",176,150,176,360); GObj.MoveGrObject("Trdline3",304,150,304,360); GObj.MoveGrObject("Trdline4",432,150,432,360); GObj.MoveGrObject("Trdline5",42,350,560,350); GObj.MoveGrObject("Trdline6",42,300,560,300); GObj.MoveGrObject("Trdline7",42,250,560,250); GObj.MoveGrObject("Trdline8",42,200,560,200); GObj.MoveGrObject("Arrline1",560,150,28,150); GObj.MoveGrObject("Arrline2",560,150,560,370); GObj.MoveGrObject("Rect1",0,1,208,110); GObj.MoveGrObject("Rect2",208,1,416,110); GObj.MoveGrObject("Rect3",416,1,624,110); GObj.MoveGrObject("Rect4",0,1,624,400); GObj.MoveGrObject("Rect5",20,10,195,80); }
gr_object_coordinate() 함수의 호출은 표시기의 OnCalculate() 함수에 포함됩니다. 새 눈금이 올 때마다 함수가 호출되기 때문에 차트에 새 막대가 형성될 때 좌표를 올바르게 다시 계산합니다.
표시기 패널의 버튼은 세 그룹으로 나뉩니다. 첫 번째 그룹은 왼쪽에 있는 4개의 버튼으로 구성됩니다. 표시기로 입력 시퀀스의 스펙트럼을 추정한 결과를 표시하는 모드를 선택할 수 있습니다. 4가지 표시 모드가 지원됩니다(버튼 수에 따라 다름).
- 진폭/선 - Y축을 따라 선형 스케일로 푸리에 변환 모듈을 표시합니다.
- Amplitude/dB - Y축을 따라 로그 스케일로 푸리에 변환 모듈을 표시합니다.
- Power/Line - Y 축을 따라 선형 스케일로 푸리에 변환 모듈의 제곱을 표시합니다.
- Power/dB - Y 축을 따라 대수 스케일로 푸리에 변환 모듈의 제곱을 표시합니다.
이 그룹의 버튼 클릭을 처리하기 위해 표시기의 OnChartEvent() 함수에 다음 코드가 포함됩니다.
if(id==CHARTEVENT_OBJECT_CLICK) // Click on the gr. object { if(sparam=="Butt1") // Click on the button { GObj.SetButtonProp("Butt1",1,Chocolate); GObj.SetButtonProp("Butt2",0,SlateGray); GObj.SetButtonProp("Butt3",0,SlateGray); GObj.SetButtonProp("Butt4",0,SlateGray); YPowerFlag=0;YdBFlag=0; } if(sparam=="Butt2") // Click on the button { GObj.SetButtonProp("Butt1",0,SlateGray); GObj.SetButtonProp("Butt2",1,Chocolate); GObj.SetButtonProp("Butt3",0,SlateGray); GObj.SetButtonProp("Butt4",0,SlateGray); YPowerFlag=0;YdBFlag=1; } if(sparam=="Butt3") // Click on the button { GObj.SetButtonProp("Butt1",0,SlateGray); GObj.SetButtonProp("Butt2",0,SlateGray); GObj.SetButtonProp("Butt3",1,Chocolate); GObj.SetButtonProp("Butt4",0,SlateGray); YPowerFlag=1;YdBFlag=0; } if(sparam=="Butt4") // Click on the button { GObj.SetButtonProp("Butt1",0,SlateGray); GObj.SetButtonProp("Butt2",0,SlateGray); GObj.SetButtonProp("Butt3",0,SlateGray); GObj.SetButtonProp("Butt4",1,Chocolate); YPowerFlag=1;YdBFlag=1; }
버튼 중 하나의 클릭이 감지되면 다른 버튼의 상태는 누르지 않은 상태로 변경되어 한 그룹의 여러 버튼을 동시에 누르는 것을 방지합니다. 동시에, 현재 표시 모드를 결정하는 플래그 YPowerFlag 및 YdBFlag에 대해 해당 값이 설정됩니다.
4개의 버튼으로 구성된 두 번째 그룹은 입력 데이터 소스를 선택할 수 있는 가능성을 제공합니다. 이는 SAInpData.mq5 표시기를 호출하여 얻은 외부 데이터이거나 애플리케이션 자체에서 생성된 세 가지 테스트 시퀀스일 수 있습니다. 마지막 버튼 그룹에는 텍스트 정보 입력 필드에서 목록을 스크롤하는 데 사용되는 두 개의 버튼이 있습니다. 모든 버튼을 클릭하는 처리는 첫 번째 그룹의 버튼과 동일한 표시기의 OnChartEvent() 기능에서도 수행됩니다. SpecAnalyzer.mq5 파일을 참조하십시오.
이전에 생성한 테스트 인디케이터 Test.mq5를 사용하여 AllGrObject 클래스를 사용하는 예를 보여드리겠습니다. 이를 수행하려면 소스 코드에 여러 줄을 추가하고 SpecAnalyzer.mq5 파일에서 앞서 언급한 gr_object_create() 및 gr_object_coordinate() 함수를 포함합니다.
//+------------------------------------------------------------------+ //| Test.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_chart_window #include "SpecAnalyzer.mqh" GRaphChart MainChart; AllGrObject GObj; // <----- Creating object of the class AllGrObject //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- gr_object_create(); // <----- creating graphical objects return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- MainChart.SetOwnProperty(); // <----- restoring current properties of the chart gr_object_coordinate(); // <----- setting coordinates for the graphical objects //--- return value of prev_calculated for next call return(rates_total); } //----------------------------------------------- Create all graphical objects -- void gr_object_create() { GObj.AddLabel("Title",10,"Arial",DarkGray,256,367,"Spectrum Analyzer"); GObj.AddLabel("Label1",9,"Arial",LightSteelBlue,557,128,"0"); GObj.AddLabel("Label2",9,"Arial",LightSteelBlue,422,128,"128"); GObj.AddLabel("Label3",9,"Arial",LightSteelBlue,294,128,"256"); GObj.AddLabel("Label4",9,"Arial",LightSteelBlue,166,128,"384"); GObj.AddLabel("Label5",9,"Arial",LightSteelBlue,40,128,"512"); GObj.AddLabel("Label6",9,"Arial",LightSteelBlue,28,156,"N"); GObj.AddLabel("Label7",9,"Arial",LightSteelBlue,569,141,"0.00"); GObj.AddLabel("Label8",9,"Arial",LightSteelBlue,569,191,"0.25"); GObj.AddLabel("Label9",9,"Arial",LightSteelBlue,569,241,"0.50"); GObj.AddLabel("Label10",9,"Arial",LightSteelBlue,569,291,"0.75"); GObj.AddLabel("Label11",9,"Arial",LightSteelBlue,569,341,"1.00"); GObj.AddLabel("Label12",9,"Arial",LightSteelBlue,569,358,"U"); GObj.AddLabel("Label13",9,"Arial",DarkGray,490,86,"Y-axis Mode"); GObj.AddLabel("Label14",9,"Arial",DarkGray,285,86,"Input Data"); GObj.AddLabel("Label15",9,"Arial",DarkGray,75,86,"Level"); GObj.AddLabel("Label16",9,"Arial",DarkGray,185,86,"N"); GObj.AddLabel("Label17",8,"Courier",DarkOliveGreen,64,64); GObj.AddLabel("Label18",8,"Courier",DarkOliveGreen,64,51); GObj.AddLabel("Label19",8,"Courier",DarkOliveGreen,64,38); GObj.AddLabel("Label20",8,"Courier",DarkOliveGreen,64,25); GObj.AddLabel("Label21",8,"Courier",DarkOliveGreen,64,12); GObj.AddButton("Butt1",185,18,C'32,32,32',8,"Arial",SlateGray,612,79,"Amplitude (line)",0); GObj.AddButton("Butt2",185,18,C'32,32,32',8,"Arial",Chocolate,612,61,"Amplitude (log)",1); GObj.AddButton("Butt3",185,18,C'32,32,32',8,"Arial",SlateGray,612,43,"Power (line)",0); GObj.AddButton("Butt4",185,18,C'32,32,32',8,"Arial",SlateGray,612,25,"Power (log)",0); GObj.AddButton("Butt5",185,18,C'32,32,32',8,"Arial",SlateGray,403,79,"External Data",0); GObj.AddButton("Butt6",185,18,C'32,32,32',8,"Arial",SlateGray,403,61,"Test 1. SMA(3)",0); GObj.AddButton("Butt7",185,18,C'32,32,32',8,"Arial",Chocolate,403,43,"Test 2. SMA(32)",1); GObj.AddButton("Butt8",185,18,C'32,32,32',8,"Arial",SlateGray,403,25,"Test 3. LWMA(12)",0); GObj.AddButton("Butt9",14,34,C'32,32,32',8,"Wingdings",SlateGray,36,78,"\x0431",0); GObj.AddButton("Butt10",14,34,C'32,32,32',8,"Wingdings",SlateGray,36,44,"\x0432",0); GObj.AddEdit("Edit1",35,18,Black,9,"Arial",SlateGray,180,102); GObj.AddTrendLine("Trdline1",C'32,32,32'); GObj.AddTrendLine("Trdline2",C'32,32,32'); GObj.AddTrendLine("Trdline3",C'32,32,32'); GObj.AddTrendLine("Trdline4",C'32,32,32'); GObj.AddTrendLine("Trdline5",C'32,32,32'); GObj.AddTrendLine("Trdline6",C'32,32,32'); GObj.AddTrendLine("Trdline7",C'32,32,32'); GObj.AddTrendLine("Trdline8",C'32,32,32'); GObj.AddArrowline("Arrline1",LightSteelBlue); GObj.AddArrowline("Arrline2",LightSteelBlue); GObj.AddRectangle("Rect1",C'72,72,72'); GObj.AddRectangle("Rect2",C'72,72,72'); GObj.AddRectangle("Rect3",C'72,72,72'); GObj.AddRectangle("Rect4",DarkGray); GObj.AddRectangle("Rect5",C'72,72,72'); } //---------- Set object coordinate for Trend Line, Arroved Line and Rectangle -- void gr_object_coordinate() { GObj.MoveGrObject("Trdline1",48,150,48,360); GObj.MoveGrObject("Trdline2",176,150,176,360); GObj.MoveGrObject("Trdline3",304,150,304,360); GObj.MoveGrObject("Trdline4",432,150,432,360); GObj.MoveGrObject("Trdline5",42,350,560,350); GObj.MoveGrObject("Trdline6",42,300,560,300); GObj.MoveGrObject("Trdline7",42,250,560,250); GObj.MoveGrObject("Trdline8",42,200,560,200); GObj.MoveGrObject("Arrline1",560,150,28,150); GObj.MoveGrObject("Arrline2",560,150,560,370); GObj.MoveGrObject("Rect1",0,1,208,110); GObj.MoveGrObject("Rect2",208,1,416,110); GObj.MoveGrObject("Rect3",416,1,624,110); GObj.MoveGrObject("Rect4",0,1,624,400); GObj.MoveGrObject("Rect5",20,10,195,80); } //+------------------------------------------------------------------+
AllGrObject 클래스의 기능에 대한 액세스를 제공하려면 이 클래스의 GObj 개체를 만듭니다. 표시기의 OnInit() 함수에는 표시기 제어판의 모양과 기능을 결정하는 필요한 모든 그래픽 개체를 만드는 gr_object_create() 함수의 호출이 포함됩니다.
OnCalculate 함수에서 MainChart.SetOwnProperty() 및 gr_object_coordinate() 함수의 호출을 추가합니다. 따라서 차트 속성과 차트에 그려진 개체의 좌표는 모든 새 눈금이 올 때마다 복원됩니다. 이러한 복원은 초기 차트에 새로운 막대가 생성되거나, 마우스 왼쪽 버튼을 이용하여 차트를 이동하거나, 사용자가 차트 속성을 변경할 때 필요합니다. 이 테스트 예제를 컴파일하고 로드하면 제어판의 인터페이스가 표시됩니다. 그림 참조. 1.
그림 1. 제어판의 인터페이스입니다.
차트에 대한 제어판의 포지션을 시각적으로 보여주기 위해 위의 예에서 차트 눈금 표시를 활성화합니다. 그림 참조. 2.
그림 2. 차트 규모입니다.
스펙트럼 분석기
표시기의 스펙트럼 분석은 입력 시퀀스의 1024 눈금으로 수행됩니다. 스펙트럼 분석은 고속 푸리에 변환 알고리즘을 사용하여 수행됩니다. FFT 알고리즘을 구현하는 기능은 www.mql4.com 웹사이트의 출판물에서 가져왔습니다. 계산을 위해 입력 실시간 시퀀스의 FFT 기능을 사용합니다. 해당 코드는 SpecAnalyzer.mqh 파일에 있습니다. 스펙트럼 분석에 필요한 모든 작업은 fft_calc() 함수에서 구현됩니다.
void fft_calc() { int i,k; realfastfouriertransform(InpData,ArraySize(InpData),false); // FFT for(i=1;i<ArraySize(Spectrum);i++) { k=i*2; Spectrum[i]=InpData[k]*InpData[k]+InpData[k+1]*InpData[k+1]; } Spectrum[0]=0.0; // Clear constant component level }
fft_calc() 함수가 호출되면 InpData[] 배열에 분석할 입력 시퀀스가 포함되어야 합니다. realfastfouriertransform()이 실행된 후 FFT의 결과가 해당 배열에 배치됩니다. 또한 모듈로의 제곱은 스펙트럼 추정치의 실수부와 허수부로부터 각 고조파에 대해 계산됩니다. 결과는 Spectrum[] 배열에 기록됩니다. Spectrum[] 배열의 요소 인덱스는 고조파 번호에 해당합니다. 상수 성분의 계산된 값은 표시기에 사용되지 않으므로 항상 0 값이 배열의 Spectrum[0] 요소에 할당됩니다.
변수 InputSource의 값에 따라 배열 InpData[]는 테스트 시퀀스 또는 외부 표시기에서 얻은 데이터로 채워질 수 있습니다. 입력 데이터는 get_input_data() 함수에서 형성됩니다.
void get_input_data() { int i; ArrayInitialize(InpData,0.0); switch(InputSource) { case 0: // External Data if(ExtHandle!=INVALID_HANDLE) CopyBuffer(ExtHandle,0,0,ArraySize(InpData),InpData); break; case 1: // Test 1. SMA(3) for(i=0;i<3;i++)InpData[i]=1; break; case 2: // Test 2. SMA(32) for(i=0;i<32;i++)InpData[i]=1; break; case 3: // Test 3. LWMA(12) for(i=0;i<12;i++)InpData[i]=12-i; break; } }
InputSource의 값이 0이면 SAInpData.mq5 표시기의 제로 버퍼에서 1024개의 값이 입력 배열 InpData[]로 복사됩니다. 분석을 위한 데이터는 지표 자체에서 또는 다른 지표를 호출하여 구성할 수 있습니다. 표시기 SAInpData.mq5에 대한 액세스를 제공하기 위해 다음 행이 OnInit() 함수에 추가되어 ExtHandle 변수의 값을 결정합니다.
int OnInit() { button. button. button. ExtHandle=iCustom(NULL,0,"SAInpData"); // External indicator Handle return(0); }
표시기 SAInpData.mq5는 클라이언트 터미널의 \MQL5\Indicators 디렉토리에 있어야 합니다. 예를 들어 이 기사에 첨부된 표시기 SAInpData.mq5는 임의의 시퀀스를 분석기에 전달합니다. SAInpData.mq5가 분석기에 다른 시퀀스를 전달하도록 하려면 소스 코드를 변경하십시오.
함수 get_input_data()에 대한 테스트 시퀀스로 SMA(3), SMA(32) 및 LWMA(12) 이동 평균의 임펄스 특성이 생성됩니다. 필터의 임펄스 특성의 푸리에 변환이 해당 필터의 진폭-주파수 특성에 해당한다는 점을 고려하여 테스트 시퀀스로 선택하면 이동 평균의 진폭-주파수 특성을 관찰할 수 있습니다.
Spectrum[] 배열에 저장된 스펙트럼 추정 결과를 정규화하고 지정된 모드에서 표시하도록 준비하기 위해 norm_and_draw() 함수가 사용됩니다. SpecAnalyzer.mq5 파일을 참조하십시오. 선택한 표시 모드에 따라 이 기능은 Y축의 텍스트 표시를 대체합니다.
입력 시퀀스의 스펙트럼 추정 결과는 그래픽 형태뿐만 아니라 텍스트 형태로도 표시됩니다. 결과를 텍스트 형식으로 표현하기 위해 "Label" 유형의 5가지 그래픽 개체가 생성됩니다. 5개의 표시된 텍스트 줄에 해당합니다. list_levels() 함수는 이러한 행을 정보로 채우는 작업을 수행합니다.
void list_levels() { int m; string str; if(YdBFlag==0) str="%3u %.5f"; // If Y-axis mode = Line else str="%3u %6.1f dB"; // If Y-axis mode = dB m=ArraySize(ListArray)-5; if(ListPointer<1)ListPointer=1; if(ListPointer>m)ListPointer=m; GObj.LabelTxt("Label17",StringFormat(str,ListPointer,ListArray[ListPointer])); GObj.LabelTxt("Label18",StringFormat(str,ListPointer+1,ListArray[ListPointer+1])); GObj.LabelTxt("Label19",StringFormat(str,ListPointer+2,ListArray[ListPointer+2])); GObj.LabelTxt("Label20",StringFormat(str,ListPointer+3,ListArray[ListPointer+3])); GObj.LabelTxt("Label21",StringFormat(str,ListPointer+4,ListArray[ListPointer+4])); }
행은 StringFormat() 함수를 사용하여 형식이 지정된 배열 ListArray[]의 레벨 값을 표시합니다. 현재 표시 모드에 따르면 이 배열은 norm_and_draw() 함수 내부의 정보로 채워져 있습니다. SpecAnalyzer.mq5 파일을 참조하십시오. 배열 ListArray[]의 정보는 ListPointer 변수에 저장된 값과 동일한 배열 인덱스부터 표시됩니다. 출력 필드 오른쪽에 있는 버튼을 누르거나 입력 필드에 필요한 인덱스를 지정하여 ListPointer 변수의 값을 변경할 수 있으며, 따라서 표시할 줄의 시작 인덱스를 변경할 수 있습니다. 해당 버튼을 누르고 입력 필드에서 수정을 완료하는 것과 관련된 이벤트는 표시기의 OnChartEvent() 함수에서 처리됩니다. SpecAnalyzer.mq5 파일을 참조하십시오.
표시기 SpecAnalyzer의 모양은 아래 그림과 같습니다.
그림 3. 표시기 SpecAnalyzer의 모양입니다.
결론
이미 언급했듯이 SpecAnalyzer.mq5 표시기는 완전한 스펙트럼 분석기의 프로토타입일 뿐입니다. 기사에서는 그래픽 개체를 사용하는 예로 사용됩니다. 완전한 지표를 만들려면 모양을 개선하고 더 기능적인 인터페이스를 구현하고 스펙트럼 추정 알고리즘을 개선해야 할 것입니다.
장식용으로 그래픽 개체 "Bitmap"을 사용하고, 그래픽 편집기에서 전면 제어판용 이미지를 생성하고, 이를 언더레이어로 사용하여 표시기 인터페이스를 크게 개선할 수 있습니다. 제어가 표시됩니다. 이러한 접근 방식은 스킨 변경이 가능한 지표를 만드는 데 사용할 수 있습니다.
문학
- 사토 유키오. 신호 관리 소개.
- L. Rabiner, B. 골드. 디지털 신호 처리의 이론과 응용.
- S.L. 마플 주니어 응용 프로그램을 사용한 디지털 스펙트럼 분석.
파일
- SpecAnalyzer.mq5 – 이 기사에서 설명하는 지표.
- SpecAnalyzer.mqh – SpecAnalyzer.mq5의 포함 파일입니다.
- SAInpData.mq5 – 외부 데이터에 대한 액세스 구성에 사용되는 지표입니다.
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/185