English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
HTML의 차트 및 다이어그램

HTML의 차트 및 다이어그램

MetaTrader 5 | 4 8월 2021, 17:03
288 0
Victor
Victor


소개

대부분의 경우 MetaTrader 5는 완전한 자급자족 제품이며 추가 확장이 필요하지 않습니다. MetaTrader 5는 브로커와의 연결을 제공하고, 시세를 표시하고, 시장 분석을 위한 다양한 지표를 사용할 수 있도록 하며, 물론 거래자에게 거래 작업을 할 수 있는 기회를 제공합니다. MetaTrader 5는 주로 거래를 편안하게 만드는 데 중점을 두고 있기 때문에 연구, 수학적 방법 분석, 멀티미디어 콘텐츠 제작 등을 위해 설계된 절대적으로 보편적인 도구가 될 수 없으며 기술적으로도 안됩니다.

또한 소프트웨어 제품의 과도한 보편성은 궁극적으로 효율성, 신뢰성, 보안성을 저하시킵니다. 반면에 특정 경우에 사용자는 몇 가지 추가 기능이 필요할 수 있습니다. 특히 거래자는 다양한 전문 분야와 교육 배경을 가진 사람들입니다. 따라서 추가 기능은 물론 안정성과 안전성을 희생하지 않고 상당히 간단한 방법으로 달성되는 경우 거래 플랫폼의 매력을 높일 수 있습니다.

이 글에서는 터미널에서 얻은 데이터를 기반으로 차트와 다이어그램을 만들고 표시할 수 있는 기회를 제공하는 이러한 추가 기능 중 하나를 고려할 것입니다.

각 프로그램은 최선을 다해야 합니다. 이 원칙을 고수한다면 MetaTrader 5가 브로커와의 거래, 수신 정보 수집 및 처리를 담당하고 이 정보의 그래픽 표시를 위해 이러한 목적을 위한 다른 프로그램을 사용하도록 합시다.


웹 브라우저

오늘날에는 웹 브라우저가 설치되지 않은 컴퓨터를 찾기가 어렵습니다. 오랫동안 브라우저는 진화하고 개선되어 왔습니다. 최신 브라우저는 매우 안정적이고 안정적이며 가장 중요한 것은 무료입니다. WEB-browser는 사실상 인터넷에 접속하기 위한 기본적인 도구라는 점을 감안할 때 대부분의 사용자가 익숙하고 사용에 어려움이 거의 없습니다.

최신 브라우저의 기능은 너무 광범위하여 웹 브라우저를 통해 비디오를 보고, 음악을 듣고, 게임을 하고, 기타 여러 활동을 하는 데 익숙해졌습니다. 따라서 오늘날 웹 브라우저는 다양한 형식으로 표시될 수 있는 다양한 유형의 정보를 표시하기 위해 잘 개발된 도구입니다.

현재 몇 가지 인기 있는 웹 브라우저가 있다는 점은 언급하지 않을 수 없습니다. InternetExplorer, Mozilla Firefox, Google Chrome 오페라가 여기에 해당하죠. 이러한 브라우저는 소프트웨어 구현 및 사용자 인터페이스 측면에서 서로 크게 다를 수 있습니다. 그러나 이론적으로는 주로 HTML 언어의 표준과 관련된 정보 교환을 위해 네트워크에서 채택된 기본 표준을 완벽하게 지원해야 합니다.

실제로 개발자의 노력에도 불구하고 브라우저는 특정 프로토콜이나 기술의 구현 측면에서 여전히 몇 가지 개별적인 특성을 가지고 있습니다. 특정 브라우저가 개별 기능으로 인해 우리에게 적합하지 않다고 결정하면 하나 이상의 다른 웹 브라우저를 컴퓨터에 설치하여 이 문제를 쉽게 해결할 수 있습니다. Firefox와 같은 브라우저의 열렬한 지지자조차도 시스템에 최소한 Internet Explorer가 설치되어 있습니다.

웹 브라우저가 클라이언트 부분으로 개발되어 원격 서버와의 상호 작용을 제공한다는 사실에도 불구하고 컴퓨터에 저장된 로컬 정보를 표시하는 데에도 사용할 수 있습니다. 예를 들어 이전에 컴퓨터에 저장한 웹 페이지를 볼 수 있습니다. 브라우저는 로컬 페이지 작업을 위해 인터넷에 액세스할 필요가 없습니다.

따라서 오프라인 모드에서 실행되는 웹 브라우저는 MetaTrader 5 클라이언트 터미널의 그래픽 기능을 확장하는 데 사용되는 프로그램의 역할에 매우 매력적인 후보입니다. 그것을 사용하기 위해 값비싼 구매, 번거롭고 긴 설치를 할 필요가 없으며 새로운 소프트웨어 제품을 사용하는 방법을 배울 필요도 없습니다.

따라서 이 글에서는 MetaTrader 5에서 얻은 데이터를 기반으로 차트와 다이어그램을 구성하기 위해 웹 브라우저를 사용하는 가능성을 고려할 것입니다.


HTML과 자바스크립트

웹 브라우저를 확장으로 사용하기로 선택하여 기본 규칙을 스스로 정의해 보겠습니다. 이 규칙은 앞으로 엄격하게 준수할 것입니다. 생성된 HTML 페이지의 표시는 로컬 또는 원격 WEB 서버 없이 수행되어야 합니다. 즉, 우리는 컴퓨터에 서버 소프트웨어를 설치하지 않으며 페이지를 표시하기 위해 네트워크에 액세스할 필요가 없습니다. 우리가 만드는 HTML 페이지는 웹 브라우저를 통해서만 표시되어야 하며 컴퓨터에 있어야 합니다. 이 규칙은 외부 네트워크 액세스로 인해 발생할 수 있는 보안 감소와 관련된 위험을 최소화합니다.

정보 표시를 위해 HTML 4의 기능만 사용하여 MetaTrader 5에서 받은 데이터를 기반으로 표, 서식 있는 텍스트 및 이미지가 있는 WEB 페이지를 만들 수 있지만 이러한 기회는 우리를 완전히 만족시킬 수 없습니다.

대부분의 경우 다른 사이트로 이동할 때 브라우저에서 보는 것은 HTML 확장을 사용하여 생성됩니다. 일반적으로 이러한 확장은 서버 측에서 실행되며 이러한 이유로 우리의 목적에 적합하지 않습니다. 브라우저 측에서 작동할 수 있고 서버 소프트웨어가 필요하지 않은 기술(예: Macromedia Flash, JavaScript 및 Java)이 관심의 대상이 될 수 있습니다.

브라우저 측에서 Macromedia Flash 및 Java 응용 프로그램을 실행하려면 최소한 추가 플러그인을 설치해야 하며 JavaScript로 작성된 사용자 프로그램은 브라우저에서 직접 실행됩니다. 모든 일반적인 웹 브라우저에는 자체 내장 JavaScript 인터프리터가 있습니다. 추가 소프트웨어나 플러그인을 설치하지 않아도 되도록 JavaScript를 선택하겠습니다.

따라서 다음에서는 MQL5가 있는 MetaTrader 5와 HTMLJavaScript가 있는 웹 브라우저만 사용할 것입니다. 추가 소프트웨어가 필요하지 않습니다. HTML 페이지는 텍스트 파일에 불과하다는 사실을 기억해야 합니다. 따라서 HTML 문서를 만들기 위해 모든 텍스트 편집기를 사용할 수 있습니다. 예를 들어 MetaEditor 5에서 HTML 코드를 만들고 편집할 수 있습니다. 이 글을 작성할 때 HTML 코드의 편집은 브라우저 Opera @ USB v10.63에서 이루어졌습니다. 그리고 이는 페이지 내용을 편집하고 수정된 페이지를 저장하며 표시되는 방식을 미리 볼 수 있도록 해줍니다.

HTML 및 JavaScript 언어에 익숙하지 않은 사람은 이러한 언어를 마스터하는 것과 관련하여 발생할 수 있는 어려움에 대해 합리적으로 걱정할 수 있습니다. 작업을 용이하게 하고 HTML 및 JavaScript에 대한 심층적인 연구를 피하기 위해 이 기술을 기반으로 하는 준비된 솔루션을 사용하려고 합니다. 이 글의 범위에서 우리의 목표는 차트와 다이어그램의 구성에만 국한되므로 이 목적을 위해 특별히 작성된 JavaScript 라이브러리를 사용할 것입니다.

Emprise JavaScript Charts는 조용한 고급 그래픽 라이브러리입니다. 아마도 독자는 제공된 링크를 통해 더 잘 알고 싶어할 것입니다. 그러나 이 라이브러리는 완전히 무료가 아닙니다. 따라서 Dygraphs JavaScript Visualization LibraryHighcharts 차트 라이브러리와 같은 무료 라이브러리를 살펴보겠습니다. Dygraphs는 간결함과 단순성으로 인해 매력적이며 Highcharts 라이브러리에는 더 많은 기능이 포함되어 있으며 더 보편적으로 보입니다. Highcharts 라이브러리가 약 75KB이고 추가 jQuery 라이브러리가 필요하다는 사실에도 불구하고 약 70KB가 더 필요하지만 여전히 선택한 라이브러리로 선택할 것입니다.

저희 웹사이트 http://www.highcharts.com/의 "데모 갤러리" 섹션에서 Highcharts 라이브러리에 대해 알아볼 수 있습니다. 각 예제에 대해 "옵션 보기"를 클릭하면 소스 JavaScript 코드를 볼 수 있습니다. 라이브러리에 대한 자세한 문서는 "Documentation/Options Reference" 섹션에 있으며, 이 섹션에서는 다른 옵션 사용에 대한 많은 예도 찾을 수 있습니다. 언뜻 보기에는 자바스크립트 코드가 풍부하고 MQL 프로그래머 구문에서는 보기 드문 이 라이브러리의 사용이 상당히 복잡해 보일 수 있습니다. 그러나 이것은 그렇지 않습니다. 라이브러리를 통해 차트를 표시하는 간단한 HTML 파일의 첫 번째 예를 고려하십시오.

예를 들어 메모장 편집기에서 Test_01.htm이라는 텍스트 파일을 만들고 다음과 같은 간단한 라이브러리 사용 예를 복사해 보겠습니다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<!-- - -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"
             type="text/javascript"></script>
<script src="/js/highcharts.js" type="text/javascript"></script>
<!-- - -->
<script type="text/javascript">
var chart1;
$(document).ready(function(){
  chart1 = new Highcharts.Chart({
    chart: {renderTo: 'container1'},
    series: [{data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]}]
  });
});
</script>
<!-- - -->
</head>
<body>
<div id="container1" style="width: 700px; height: 400px "></div>
</body>
</html>

샘플 코드는 주석에 의해 4개의 섹션으로 구분됩니다.

코드의 첫 번째 부분에는 일반적인 HTML 페이지 태그가 포함되어 있습니다. 코드의 이 부분은 지금 우리에게 특별한 관심이 없습니다.

그 뒤에 두 개의 태그 <script>를 포함하는 다른 부분이 옵니다. 첫 번째 경우에는 브라우저에 ajax.googleapis.com 웹사이트에서 라이브러리 코드 jquery.min.js를 다운로드하라는 명령을 제공합니다. 두 번째 경우는 서버 측에서 /js/ 카탈로그에 브라우저가 다운로드해야 하는 highcharts.js 라이브러리가 포함되어 있다고 가정합니다. 이전에 페이지를 표시하는 과정에서 외부 소스에 대한 액세스가 없어야 한다고 결정한 경우 코드의 이 부분을 변경해야 합니다.

변경한 후 코드의 이 부분은 다음과 같습니다.

<script src="jquery.min.js" type="text/javascript"></script>
<script src="highcharts.js" type="text/javascript"></script>

이 경우 HTML 파일이 있는 카탈로그, 즉 현재 카탈로그에서 두 라이브러리를 모두 다운로드하라는 명령을 제공합니다. 브라우저에서 라이브러리를 다운로드하려면 먼저 ajax.googleapis.com 및 http://www.highcharts.com에서 라이브러리를 다운로드하고 Google이 있는 동일한 카탈로그에 복사해야 합니다. HTML-파일이 있는 곳에 말이죠. 이 두 라이브러리는 이 글의 끝부분에 있는 첨부 파일에서도 찾을 수 있습니다.

코드의 다음 섹션에서는 Highcharts.Chart 클래스의 개체가 생성됩니다. "renderTo: 'container1'" 매개변수는 차트가 "container1"이라는 HTML 요소에 표시될 것임을 나타내고 매개변수 "data"는 차트에 표시될 데이터를 정의합니다. 이 예제에서 볼 수 있듯이 Highcharts.Chart 클래스의 개체를 생성하는 동안 데이터는 매개 변수와 동일한 방식으로 정의됩니다. 간단한 변경을 통해 표시된 데이터의 정의를 코드의 별도 부분에 배치합니다. 이렇게 하면 여러 차트를 표시해야 하는 경우 데이터를 그룹화할 수 있습니다.

예제의 마지막 부분에서 <div> 태그는 "container1"이라는 HTML 요소를 선언하고 이 항목의 치수가 표시됩니다. 앞서 언급했듯이 이것은 차트를 구성하는 데 사용되는 HTML 요소이며 태그 <div>에 지정된 "container1" 요소의 크기에 따라 크기가 결정됩니다.

변경된 사항을 고려하면 예제 코드는 다음과 같습니다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<!-- - -->
<script src="jquery.min.js" type="text/javascript"></script>
<script src="highcharts.js" type="text/javascript"></script>
<!-- - -->
<script type="text/javascript">
var dat1 = [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4];
</script>
<!-- - -->
<script type="text/javascript">
var chart1;
$(document).ready(function(){
  chart1 = new Highcharts.Chart({
    chart: {renderTo: 'container1'},
    series: [{data: dat1}]
  });
});
</script>
<!-- - -->
</head>
<body>
<div id="container1" style="width: 700px; height: 400px "></div>
</body>
</html>

이 테스트 케이스와 모든 라이브러리는 이 글 끝에 있는 첨부 파일에서 복사할 수 있습니다. Test_01.htm 예제 파일과 라이브러리의 파일은 동일한 \Test 폴더에 있으므로 HTML 파일 Test_01.htm을 두 번 클릭하면 작업 결과를 볼 수 있습니다.

이 테스트 페이지를 정상적으로 표시하려면 웹 브라우저에서 JavaScript 실행이 허용되어야 합니다. 브라우저에서는 보안을 위해 이 옵션을 비활성화할 수 있으므로 꺼져 있을 수 있습니다. 결과적으로 다음이 표시되어야 합니다.

Test_01.htm

그림 1. Test_01.htm 

이것은 우리의 첫 번째 테스트 차트이며 이 기술의 명백한 복잡성에도 불구하고 생성하는 데 오랜 시간이 걸리지 않았습니다.

이러한 방식으로 생성된 표시된 차트의 일부 기능에 주목해야 합니다. 복사한 카탈로그에서 Test_01.htm 파일을 열고 웹 브라우저를 사용하여 본 페이지를 확대할 수 있으면 상당히 확대해도 차트의 품질이 악화되지 않는 것을 알 수 있습니다. .

이는 이 차트가 PNG 또는 JPEG 파일과 같은 정적 이미지가 아니며 도면에 할당된 영역을 확대 또는 축소한 후 다시 스케치되기 때문입니다. 따라서 그러한 이미지는 일반적으로 우리가 좋아하는 사진을 저장하는 방식으로 디스크에 저장할 수 없습니다. 차트는 JavaScript를 사용하여 구성되었으므로 이 언어에 대한 자체 내장 인터프리터가 있는 다른 브라우저가 항상 같은 방식으로 차트를 실행하지 않을 수 있다는 사실을 언급해야 합니다.

JavaScript를 사용하여 만든 차트는 브라우저에 따라 다르게 보일 수 있습니다. 대부분의 경우 이러한 차이점은 다른 브라우저와 비교하여 Internet Explorer에서 가장 자주 발생합니다.

그러나 우리는 JavaScript 라이브러리의 작성자가 가장 인기 있는 웹 브라우저와 코드의 가능한 최대 호환성을 처리하기를 바랍니다.


MetaTrader 5 및 MQL5

위의 예에서 차트에 표시하려는 데이터는 HTML 페이지 생성 시 수동으로 설정되었습니다. MetaTrader 5에서 생성된 차트로 데이터 전송을 정렬하기 위해 가장 간단한 방법을 사용합니다. MetaTrader 5가 차트를 표시할 때 브라우저에 로드될 별도의 파일에 데이터를 기록하도록 합니다. 이 파일을 생성할 MQL5의 파일 및 스크립트에서 데이터를 다운로드하여 차트를 표시할 HTML 페이지를 포함하는 예제를 작성해 보겠습니다.

HTML 파일로 약간의 변경을 수행한 후 이전에 만든 Test_01.htm 파일을 사용합니다. 수정된 파일을 example1.htm이라고 합니다. 변경된 모든 사항은 다음 행으로 축소됩니다.

<script type="text/javascript">
var dat1 = [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4];
</script>
이는 다음으로 대체됩니다
<script type="text/javascript">
var dat1=[0];
</script>
<script src="exdat.txt" type="text/javascript"></script>

이제 브라우저는 HTML 페이지를 다운로드할 때 차트에 표시하려는 값이 dat1 배열에 할당되는 exdat.txt 텍스트 파일도 로드해야 합니다. 이 파일에는 JavaScript 코드 조각이 포함되어 있어야 합니다. 이 파일은 해당 스크립트를 사용하여 MetaTrader 5에서 쉽게 생성할 수 있습니다.

이러한 스크립트의 예가 아래에 나와 있습니다.

//+------------------------------------------------------------------+
//|                                                     Example1.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
  int i,n,fhandle;
  double gr[25];
  string str;
  
  n=ArraySize(gr);
  for(i=0;i<n;i++)
    {
    gr[i]=NormalizeDouble(MathSin(i*3*2*M_PI/n),4);
    }

  str=DoubleToString(gr[0],4);
  for(i=1;i<n;i++)
    {
    str+=","+DoubleToString(gr[i],4);
    }
  
  ResetLastError();
  fhandle=FileOpen("exdat.txt",FILE_WRITE|FILE_TXT|FILE_ANSI);
  if(fhandle<0){Print("File open failed, error ",GetLastError());return;}
  
  FileWriteString(fhandle,"dat1=["+str+"];\n");
  
  FileClose(fhandle);
  }
//+------------------------------------------------------------------+

표시된 데이터를 저장하기 위해 이 스크립트는 25개 항목을 보유하는 gr[] 배열을 사용합니다. 예를 들어 이 배열은 sinus 함수의 값으로 채워지며 소수점 이하 네 자리까지 반올림됩니다. 물론 이 배열은 더 유용한 다른 데이터로 채울 수 있습니다.

또한 이 데이터는 형식이 지정되고 단일 텍스트 문자열로 결합됩니다. 생성된 텍스트 파일의 볼륨을 줄이기 위해 gr[] 배열 요소의 값을 소수점 이하 4자리만 문자열에 넣습니다. 이를 위해 DoubleToString() 함수를 사용했습니다.

문자열 str이 형성된 후 exdat.txt 파일에 저장됩니다. 스크립트가 성공적으로 실행되면 클라이언트 터미널의 \MQL5\Files 하위 폴더에 texdat.txt 텍스트 파일이 생성됩니다. 파일이 이미 있는 경우 덮어씁니다.

jquery.min.js, highcharts.js, Example1.mq5, Example1.htm 및 exdat.txt 파일은 이 글의 끝부분에 있는 첨부 파일 섹션에 제공됩니다. 이 5개의 파일은 카탈로그 \Example1에 있습니다. 결과를 간단히 보려면 ​​이 예제를 복사하고 카탈로그 \Example1에서 Example1.htm 파일을 엽니다. 차트는 exdat.txt 파일의 데이터에 따라 작성됩니다.

Example1.htm 

그림 2. Example1.htm

물론 Example1.mq5 스크립트를 실행하려면 클라이언트 터미널의 \MQL5\Scripts 폴더에 있어야 하며 컴파일해야 합니다.

앞서 언급했듯이 스크립트 실행 후 exdat.txt 파일은 \MQL5\Files 폴더에 생성되지만 이 예에서는 HTML 파일, 라이브러리의 파일 및 데이터 파일이 모두 같은 폴더에 있어야 합니다. 따라서 jquery.min.js, highcharts.js 및 Example1.htm 파일을 \MQL5\Files 폴더에 복사하거나 exdat.txt 파일을 이러한 파일이 있는 폴더에 복사해야 합니다.

이 예에서 HTML 페이지와 라이브러리는 다른 파일에 저장됩니다. 디자인 단계에서 프로젝트의 다른 부분이 별도의 파일에 있는 것이 유용할 수 있습니다. 이것은 예를 들어 HTML 파일을 편집할 때 라이브러리 코드의 무작위 변경을 방지하는 데 도움이 됩니다. 그러나 HTML 페이지가 완전히 편집되고 더 이상의 변경이 없을 것으로 예상되면 라이브러리를 HTML 코드 파일에 직접 통합할 수 있습니다.

이것은 JavaScript 라이브러리가 단순한 텍스트 파일에 불과하기 때문에 가능합니다. jquery.min.js 또는 highcharts.js를 텍스트 편집기로 열면 라이브러리의 소스 코드가 최대 용량으로 압축되었기 때문에 이해할 수 있는 내용이 표시되지 않습니다.

압축은 서비스 기호(예: 줄 바꿈 또는 일련의 공백)를 제거하여 수행됩니다. 이러한 압축 후에는 모든 서식이 손실되지만 파일 유형이 변경되지 않기 때문에 텍스트는 텍스트로 남아 있습니다. 따라서 브라우저가 확장자가 .js인 외부 파일에서 라이브러리 코드에 연결하는지 또는 텍스트 형식인 현재 HTML 파일에서 읽는지 여부는 차이가 없습니다.

파일을 결합하려면 Example1.htm에서 줄을 바꾸십시오.

<script src="jquery.min.js" type="text/javascript"></script>
<script src="highcharts.js" type="text/javascript"></script>

with

<script type="text/javascript">

</script>

다음으로 메모장과 같은 텍스트 편집기를 사용하여 jquery.min.js 라이브러리의 파일을 열고 "모두 선택" 명령을 선택하여 파일의 내용을 복사합니다. 그런 다음 Example1.htm 파일을 열고 <script type=\"text/javascript\"> 및 </script> 태그 사이에 라이브러리의 복사한 텍스트를 붙여넣습니다. 얻은 파일을 Example2.htm으로 저장합니다. 같은 방법으로 highcharts.js 라이브러리의 내용을 이 파일에 복사하여 이전에 복사한 라이브러리의 텍스트와 </script> 태그 사이에 배치합니다.

복사의 결과로 HTML 파일의 크기가 증가하지만 이제 올바른 표시를 위해 별도의 라이브러리 파일이 필요하지 않습니다. Folder\Example2에 exdat.txt 데이터 파일이 있으면 충분합니다. 여기에는 Example2.htm 파일이 포함되어 있고 exdat.txt는 이 글의 첨부 파일 섹션에 있습니다.


그래픽 형식의 거래 내역 보고서

그래픽 정보를 표시하는 제안된 방법에 대한 보다 완전한 시연을 위해 지정된 시간 간격으로 거래 계정의 이력을 보여주는 보고서를 생성합니다. "History" 탭의 컨텍스트 메뉴에서 "Report" 명령을 선택하면 MetaTrader 5에서 생성되는 HTML 기반 보고서가 프로토타입 역할을 합니다. 이 보고서에는 하나의 표에 요약된 다양한 특성이 포함되어 있습니다. 이러한 특성이 차트와 다이어그램의 형태로 제시될 때 더 시각적이라고 가정하고 highcharts.js 그래픽 라이브러리를 사용하여 표시해 보겠습니다.

위의 예에서 차트 구성을 위해 이 버전의 highcharts.js 라이브러리에 설정된 기본 표시 매개변수를 사용했습니다.

실용적인 목적을 위해 이 옵션은 성공하지 못할 것입니다. 각각의 경우에 개별 특정 요구 사항에 맞게 차트 보기를 조정해야 하기 때문입니다. 이를 위해 highcharts.js 라이브러리는 차트나 다이어그램에 적용할 수 있는 많은 옵션을 포함하여 다양한 기회를 제공합니다. 이미 언급했듯이 옵션 목록과 자세한 설명 및 예는 http://www.highcharts.com에서 찾을 수 있습니다.

이 글은 MetaTrader 5에서 받은 정보를 표시하기 위해 웹 브라우저를 사용하는 기능을 제안하고 보여주기 위한 것이므로 그래픽 라이브러리의 옵션과 그 사용의 세부 사항에 대한 설명은 생략합니다. 특히 WEB 페이지 생성을 위한 특정 요구 사항에 따라 다른 JavaScript 라이브러리를 사용할 수 있습니다. 독자는 가장 적합한 라이브러리를 독립적으로 선택하고 사용 실습에 필요한 만큼 심층적으로 연구할 수 있습니다.

거래 계정의 기록을 표시하기 위해 ProfitReport.htm 파일을 만들었습니다. 첨부파일에서 확인하실 수 있습니다. \Report 폴더에는 표시할 데이터가 있는 data.txt가 들어있습니다. data.txt 파일은 예제로 폴더에 배치됩니다.

\Report 폴더를 복사하고 ProfitReport.htm을 열면 이 예를 위해 생성된 테스트 계정의 거래 특성이 그래픽 형식으로 표시됩니다.

ProfitReport.htm

그림 3. ProfitReport.htm

ProfitReport.htm을 만들 때 먼저 대략적인 페이지 레이아웃을 만들고 대략적인 위치와 정보 유형을 결정했습니다.

그런 다음 기본 옵션이 있는 차트를 페이지에 배치했습니다.

이 템플릿을 만든 후 각 개별 차트에 가장 적합한 옵션을 선택했습니다. 편집이 끝나면 라이브러리의 텍스트를 페이지에 복사하기만 하면 됩니다. 이미 언급했듯이 올바른 페이지 표시를 위해서는 표시할 데이터가 포함된 data.txt 파일과 동일한 카탈로그에 있어야 합니다.

data.txt 파일은 ProfitReport.mq5 스크립트를 사용하여 MetaTrader 5에서 생성되었습니다. 이 스크립트가 성공적으로 실행되면 현재 활성 계정의 거래 특성이 포함된 data.txt 파일이 \MQL5\Files 폴더에 생성됩니다.

스크립트를 \MQL5\Scripts 폴더에 넣고 컴파일해야 한다는 사실을 잊어서는 안됩니다.

//-----------------------------------------------------------------------------------
//                                                                   ProfitReport.mq5
//                                          Copyright 2011, MetaQuotes Software Corp.
//                                                                https://www.mql5.com
//-----------------------------------------------------------------------------------
#property copyright   "Copyright 2011, MetaQuotes Software Corp."
#property link        "https://www.mql5.com"
#property version     "1.00"
#property script_show_inputs

#include <Arrays\ArrayLong.mqh>
#include <Arrays\ArrayDouble.mqh>
#include <Arrays\ArrayString.mqh>
#include <Arrays\ArrayInt.mqh>

//--- input parameters
input int nD=30;               // Number of days
//--- global
double   balabce_cur=0;        // balance
double   initbalance_cur=0;    // Initial balance (not including deposits to the account)
int      days_num;             // number of days in the report (including the current day)
datetime tfrom_tim;            // Date from
datetime tend_tim;             // Date to
double   netprofit_cur=0;      // Total Net Profit
double   grossprofit_cur=0;    // Gross Profit
double   grossloss_cur=0;      // Gross Loss
int      totaltrades_num=0;    // Total Trades
int      longtrades_num=0;     // Number of Long Trades
double   longtrades_perc=0;    // % of Long Trades
int      shorttrades_num=0;    // Number of Short Trades
double   shorttrades_perc=0;   // % of Short Trades
int      proftrad_num=0;       // Number of All Profit Trades
double   proftrad_perc=0;      // % of All Profit Trades
int      losstrad_num=0;       // Number of All Loss Trades
double   losstrad_perc=0;      // % of All Loss Trades
int      shortprof_num=0;      // Number of Short Profit Trades
double   shortprof_perc=0;     // % of Short Profit Trades
double   shortloss_perc=0;     // % of Short Loss Trades
int      longprof_num=0;       // Number of Long Profit Trades
double   longprof_perc=0;      // % of Long Profit Trades
double   longloss_perc=0;      // % of Long Loss Trades
int      maxconswins_num=0;    // Number of Maximum consecutive wins
double   maxconswins_cur=0;    // Maximum consecutive wins ($)
int      maxconsloss_num=0;    // Number of Maximum consecutive losses
double   maxconsloss_cur=0;    // Maximum consecutive losses ($)
int      aveconswins_num=0;    // Number of Average consecutive wins
double   aveconswins_cur=0;    // Average consecutive wins ($)
int      aveconsloss_num=0;    // Number of Average consecutive losses
double   aveconsloss_cur=0;    // Average consecutive losses ($)
double   largproftrad_cur=0;   // Largest profit trade
double   averproftrad_cur=0;   // Average profit trade
double   larglosstrad_cur=0;   // Largest loss trade
double   averlosstrad_cur=0;   // Average loss trade
double   profitfactor=0;       // Profit Factor
double   expectpayoff=0;       // Expected Payoff
double   recovfactor=0;        // Recovery Factor
double   sharperatio=0;        // Sharpe Ratio
double   ddownabs_cur=0;       // Balance Drawdown Absolute
double   ddownmax_cur=0;       // Balance Drawdown Maximal
double   ddownmax_perc=0;      // % of Balance Drawdown Maximal
int      symbols_num=0;        // Numbre of Symbols
  
string       Band="";
double       Probab[33],Normal[33];
CArrayLong   TimTrad;
CArrayDouble ValTrad;
CArrayString SymNam;
CArrayInt    nSymb;

//-----------------------------------------------------------------------------------
// Script program start function
//-----------------------------------------------------------------------------------
void OnStart()
  {
  int         i,n,m,k,nwins=0,nloss=0,naverw=0,naverl=0,nw=0,nl=0;
  double      bal,sum,val,p,stdev,vwins=0,vloss=0,averwin=0,averlos=0,pmax=0;
  MqlDateTime dt;
  datetime    ttmp,it;
  string      symb,br;
  ulong       ticket;
  long        dtype,entry;
  
  if(!TerminalInfoInteger(TERMINAL_CONNECTED)){printf("Terminal not connected.");return;}
  days_num=nD;
  if(days_num<1)days_num=1;             // number of days in the report (including the current day)
  tend_tim=TimeCurrent();                                                // date to
  tfrom_tim=tend_tim-(days_num-1)*86400;
  TimeToStruct(tfrom_tim,dt);
  dt.sec=0; dt.min=0; dt.hour=0;
  tfrom_tim=StructToTime(dt);                                            // date from
//---------------------------------------- Bands
  ttmp=tfrom_tim;
  br="";
  if(dt.day_of_week==6||dt.day_of_week==0)
    {
    Band+=(string)(ulong)(ttmp*1000)+",";
    br=",";ttmp+=86400;
    }
  for(it=ttmp;it<tend_tim;it+=86400)
    {
    TimeToStruct(it,dt);
    if(dt.day_of_week==6){Band+=br+(string)(ulong)(it*1000)+","; br=",";}
    if(dt.day_of_week==1&&br==",") Band+=(string)(ulong)(it*1000);
    }
  if(dt.day_of_week==6||dt.day_of_week==0) Band+=(string)(ulong)(tend_tim*1000);

//----------------------------------------
  balabce_cur=AccountInfoDouble(ACCOUNT_BALANCE);                          // Balance

  if(!HistorySelect(tfrom_tim,tend_tim)){Print("HistorySelect failed");return;}
  n=HistoryDealsTotal();                                           // Number of Deals
  for(i=0;i<n;i++)
    {
    ticket=HistoryDealGetTicket(i);
    entry=HistoryDealGetInteger(ticket,DEAL_ENTRY);
    if(ticket>=0&&(entry==DEAL_ENTRY_OUT||entry==DEAL_ENTRY_INOUT))
      {
      dtype=HistoryDealGetInteger(ticket,DEAL_TYPE);
      if(dtype==DEAL_TYPE_BUY||dtype==DEAL_TYPE_SELL)
        {
        totaltrades_num++;                                          // Total Trades
        val=HistoryDealGetDouble(ticket,DEAL_PROFIT);
        val+=HistoryDealGetDouble(ticket,DEAL_COMMISSION);
        val+=HistoryDealGetDouble(ticket,DEAL_SWAP);
        netprofit_cur+=val;                                         // Total Net Profit
        if(-netprofit_cur>ddownabs_cur)ddownabs_cur=-netprofit_cur; // Balance Drawdown Absolute
        if(netprofit_cur>pmax)pmax=netprofit_cur;
        p=pmax-netprofit_cur;
        if(p>ddownmax_cur)
          {
          ddownmax_cur=p;                                 // Balance Drawdown Maximal
          ddownmax_perc=pmax;
          }
        if(val>=0)              //win
          {
          grossprofit_cur+=val;                            // Gross Profit 
          proftrad_num++;                                  // Number of Profit Trades
          if(val>largproftrad_cur)largproftrad_cur=val;    // Largest profit trade
          nwins++;vwins+=val;
          if(nwins>=maxconswins_num)
            {
            maxconswins_num=nwins;
            if(vwins>maxconswins_cur)maxconswins_cur=vwins;
            }
          if(vloss>0){averlos+=vloss; nl+=nloss; naverl++;}
          nloss=0;vloss=0;
          }
        else                    //loss
          {
          grossloss_cur-=val;                                   // Gross Loss
          if(-val>larglosstrad_cur)larglosstrad_cur=-val;       // Largest loss trade
          nloss++;vloss-=val;
          if(nloss>=maxconsloss_num)
            {
            maxconsloss_num=nloss;
            if(vloss>maxconsloss_cur)maxconsloss_cur=vloss;
            }
          if(vwins>0){averwin+=vwins; nw+=nwins; naverw++;}
          nwins=0;vwins=0;
          }
        if(dtype==DEAL_TYPE_SELL)
          {
          longtrades_num++;                          // Number of Long Trades
          if(val>=0)longprof_num++;                  // Number of Long Profit Trades
          }
        else if(val>=0)shortprof_num++;               // Number of Short Profit Trades

        symb=HistoryDealGetString(ticket,DEAL_SYMBOL);   // Symbols
        k=1;
        for(m=0;m<SymNam.Total();m++)
          {
          if(SymNam.At(m)==symb)
            {
            k=0;
            nSymb.Update(m,nSymb.At(m)+1);
            }
          }
        if(k==1)
          {
          SymNam.Add(symb);
          nSymb.Add(1);
          }
        
        ValTrad.Add(val);
        TimTrad.Add(HistoryDealGetInteger(ticket,DEAL_TIME));
        }
      }
    }
  if(vloss>0){averlos+=vloss; nl+=nloss; naverl++;}
  if(vwins>0){averwin+=vwins; nw+=nwins; naverw++;}
  initbalance_cur=balabce_cur-netprofit_cur;
  if(totaltrades_num>0)
    {
    longtrades_perc=NormalizeDouble((double)longtrades_num/totaltrades_num*100,1);     // % of Long Trades
    shorttrades_num=totaltrades_num-longtrades_num;                                 // Number of Short Trades
    shorttrades_perc=100-longtrades_perc;                                           // % of Short Trades
    proftrad_perc=NormalizeDouble((double)proftrad_num/totaltrades_num*100,1);         // % of Profit Trades
    losstrad_num=totaltrades_num-proftrad_num;                                      // Number of Loss Trades
    losstrad_perc=100-proftrad_perc;                                                // % of All Loss Trades
    if(shorttrades_num>0)
      {
      shortprof_perc=NormalizeDouble((double)shortprof_num/shorttrades_num*100,1);     // % of Short Profit Trades
      shortloss_perc=100-shortprof_perc;                                            // % of Short Loss Trades
      }
    if(longtrades_num>0)
      {
      longprof_perc=NormalizeDouble((double)longprof_num/longtrades_num*100,1);        // % of Long Profit Trades
      longloss_perc=100-longprof_perc;                                              // % of Long Loss Trades
      }
    if(grossloss_cur>0)profitfactor=NormalizeDouble(grossprofit_cur/grossloss_cur,2);  // Profit Factor
    if(proftrad_num>0)averproftrad_cur=NormalizeDouble(grossprofit_cur/proftrad_num,2);// Average profit trade
    if(losstrad_num>0)averlosstrad_cur=NormalizeDouble(grossloss_cur/losstrad_num,2);  // Average loss trade
    if(naverw>0)
      {
      aveconswins_num=(int)NormalizeDouble((double)nw/naverw,0);
      aveconswins_cur=NormalizeDouble(averwin/naverw,2);
      }
    if(naverl>0)
      {
      aveconsloss_num=(int)NormalizeDouble((double)nl/naverl,0);
      aveconsloss_cur=NormalizeDouble(averlos/naverl,2);
      }
    p=initbalance_cur+ddownmax_perc;
    if(p!=0)
      {
      ddownmax_perc=NormalizeDouble(ddownmax_cur/p*100,1); // % of Balance Drawdown Maximal
      }
    if(ddownmax_cur>0)recovfactor=NormalizeDouble(netprofit_cur/ddownmax_cur,2); // Recovery Factor

    expectpayoff=netprofit_cur/totaltrades_num;                    // Expected Payoff
    
    sum=0;
    val=balabce_cur;
    for(m=ValTrad.Total()-1;m>=0;m--)
      {
      bal=val-ValTrad.At(m);
      p=val/bal;
      sum+=p;
      val=bal;
      }
    sum=sum/ValTrad.Total();
    stdev=0;
    val=balabce_cur;
    for(m=ValTrad.Total()-1;m>=0;m--)
      {
      bal=val-ValTrad.At(m);
      p=val/bal-sum;
      stdev+=p*p;
      val=bal;
      }
    stdev=MathSqrt(stdev/ValTrad.Total());
    if(stdev>0)sharperatio=NormalizeDouble((sum-1)/stdev,2);    // Sharpe Ratio

    stdev=0;
    for(m=0;m<ValTrad.Total();m++)
      {
      p=ValTrad.At(m)-expectpayoff;
      stdev+=p*p;
      }
    stdev=MathSqrt(stdev/ValTrad.Total());                      // Standard deviation
    if(stdev>0)
      {
      ArrayInitialize(Probab,0.0);
      for(m=0;m<ValTrad.Total();m++)                           // Histogram
        {
        i=16+(int)NormalizeDouble((ValTrad.At(m)-expectpayoff)/stdev,0);
        if(i>=0 && i<ArraySize(Probab))Probab[i]++;
        }
      for(m=0;m<ArraySize(Probab);m++) Probab[m]=NormalizeDouble(Probab[m]/totaltrades_num,5);
      }
    expectpayoff=NormalizeDouble(expectpayoff,2);                  // Expected Payoff  
    k=0;
    symbols_num=SymNam.Total();                                  // Symbols
    for(m=0;m<(6-symbols_num);m++)
      {
      if(k==0)
        {
        k=1;
        SymNam.Insert("",0);
        nSymb.Insert(0,0);
        }
      else
        {
        k=1;
        SymNam.Add("");
        nSymb.Add(0);
        }
      }
    }
  p=1.0/MathSqrt(2*M_PI)/4.0;
  for(m=0;m<ArraySize(Normal);m++)                             // Normal distribution
    {
    val=(double)m/4.0-4;
    Normal[m]=NormalizeDouble(p*MathExp(-val*val/2),5);
    }

  filesave();
  }
//-----------------------------------------------------------------------------------
// Save file
//-----------------------------------------------------------------------------------
void filesave()
  {
  int n,fhandle;
  string loginame,str="",br="";
  double sum;
  
  ResetLastError();
  fhandle=FileOpen("data.txt",FILE_WRITE|FILE_TXT|FILE_ANSI);
  if(fhandle<0){Print("File open failed, error ",GetLastError());return;}
  
  loginame="\""+(string)AccountInfoInteger(ACCOUNT_LOGIN)+", "+
                        TerminalInfoString(TERMINAL_COMPANY)+"\"";
  str+="var PName="+loginame+";\n";
  str+="var Currency=\""+AccountInfoString(ACCOUNT_CURRENCY)+"\";\n";
  str+="var Balance="+(string)balabce_cur+";\n";
  str+="var IniBalance="+(string)initbalance_cur+";\n";
  str+="var nDays="+(string)days_num+";\n";
  str+="var T1="+(string)(ulong)(tfrom_tim*1000)+";\n";
  str+="var T2="+(string)(ulong)(tend_tim*1000)+";\n";
  str+="var NetProf="+DoubleToString(netprofit_cur,2)+";\n";
  str+="var GrossProf="+DoubleToString(grossprofit_cur,2)+";\n";
  str+="var GrossLoss="+DoubleToString(grossloss_cur,2)+";\n";
  str+="var TotalTrad="+(string)totaltrades_num+";\n";
  str+="var NProfTrad="+(string)proftrad_num+";\n";
  str+="var ProfTrad="+DoubleToString(proftrad_perc,1)+";\n";
  str+="var NLossTrad="+(string)losstrad_num+";\n";
  str+="var LossTrad="+DoubleToString(losstrad_perc,1)+";\n";
  str+="var NLongTrad="+(string)longtrades_num+";\n";
  str+="var LongTrad="+DoubleToString(longtrades_perc,1)+";\n";
  str+="var NShortTrad="+(string)shorttrades_num+";\n";
  str+="var ShortTrad="+DoubleToString(shorttrades_perc,1)+";\n";
  str+="var ProfLong ="+DoubleToString(longprof_perc,1)+";\n";
  str+="var LossLong ="+DoubleToString(longloss_perc,1)+";\n";
  FileWriteString(fhandle,str); str="";
  str+="var ProfShort="+DoubleToString(shortprof_perc,1)+";\n";
  str+="var LossShort="+DoubleToString(shortloss_perc,1)+";\n";
  str+="var ProfFact="+DoubleToString(profitfactor,2)+";\n";
  str+="var LargProfTrad="+DoubleToString(largproftrad_cur,2)+";\n";
  str+="var AverProfTrad="+DoubleToString(averproftrad_cur,2)+";\n";
  str+="var LargLosTrad="+DoubleToString(larglosstrad_cur,2)+";\n";
  str+="var AverLosTrad="+DoubleToString(averlosstrad_cur,2)+";\n";
  str+="var NMaxConsWin="+(string)maxconswins_num+";\n";
  str+="var MaxConsWin="+DoubleToString(maxconswins_cur,2)+";\n";
  str+="var NMaxConsLos="+(string)maxconsloss_num+";\n";
  str+="var MaxConsLos="+DoubleToString(maxconsloss_cur,2)+";\n";
  str+="var NAveConsWin="+(string)aveconswins_num+";\n";
  str+="var AveConsWin="+DoubleToString(aveconswins_cur,2)+";\n";
  str+="var NAveConsLos="+(string)aveconsloss_num+";\n";
  str+="var AveConsLos="+DoubleToString(aveconsloss_cur,2)+";\n";
  str+="var ExpPayoff="+DoubleToString(expectpayoff,2)+";\n";
  str+="var AbsDD="+DoubleToString(ddownabs_cur,2)+";\n";
  str+="var MaxDD="+DoubleToString(ddownmax_cur,2)+";\n";
  str+="var RelDD="+DoubleToString(ddownmax_perc,1)+";\n";
  str+="var RecFact="+DoubleToString(recovfactor,2)+";\n";
  str+="var Sharpe="+DoubleToString(sharperatio,2)+";\n";
  str+="var nSymbols="+(string)symbols_num+";\n";
  FileWriteString(fhandle,str);

  str="";br="";
  for(n=0;n<ArraySize(Normal);n++)
    {
    str+=br+"["+DoubleToString(((double)n-16)/4.0,2)+","+DoubleToString(Normal[n],5)+"]";
    br=",";
    }
  FileWriteString(fhandle,"var Normal=["+str+"];\n");

  str="";
  str="[-4.25,0]";
  for(n=0;n<ArraySize(Probab);n++)
    {
    if(Probab[n]>0)
      {
      str+=",["+DoubleToString(((double)n-16)/4.0,2)+","+DoubleToString(Probab[n],5)+"]";
      }
    }
  str+=",[4.25,0]";
  FileWriteString(fhandle,"var Probab=["+str+"];\n");

  str=""; sum=0;
  if(ValTrad.Total()>0)
    {
    sum+=ValTrad.At(0);
    str+="["+(string)(ulong)(TimTrad.At(0)*1000)+","+DoubleToString(sum,2)+"]";
    for(n=1;n<ValTrad.Total();n++)
      {
      sum+=ValTrad.At(n);
      str+=",["+(string)(ulong)(TimTrad.At(n)*1000)+","+DoubleToString(sum,2)+"]";
      }
    }
  FileWriteString(fhandle,"var Prof=["+str+"];\n");
  FileWriteString(fhandle,"var Band=["+Band+"];\n");

  str="";br="";
  for(n=0;n<SymNam.Total();n++)
    {
    str+=br+"{name:\'"+SymNam.At(n)+"\',data:["+(string)nSymb.At(n)+"]}";
    br=",";
    }
  FileWriteString(fhandle,"var Sym=["+str+"];\n");

  FileClose(fhandle);
  }

우리가 볼 수 있듯이 스크립트 코드는 다소 번거롭지만 이것은 작업의 복잡성 때문이 아니라 가치를 결정해야 하는 많은 거래 특성 때문입니다. 이러한 값을 저장하기 위해 스크립트의 시작 부분은 관련 주석과 함께 제공된 전역 변수를 선언합니다.

OnStart() 함수는 터미널이 거래 서버에 연결되어 있는지 확인하고 연결되지 않은 경우 스크립트가 작업을 완료합니다. 서버에 연결되지 않으면 활성 계정을 정의하고 이에 대한 정보를 얻을 수 없습니다.

다음 단계는 현재 활성 계정에 대한 거래 데이터가 보고서에 포함될 날짜를 계산하는 것입니다. 종료 날짜는 현재 날짜 값과 스크립트 실행 시점의 현재 시간 값을 사용합니다. 보고서에 포함된 일 수는 기본적으로 30일인 "일 수" 입력 매개변수를 변경하여 스크립트를 로드할 때 설정할 수 있습니다. 보고서의 시작 및 종료 시간을 정의하고 나면 문자열 변수 Band에서 주말의 시작과 끝에 해당하는 한 쌍의 시간 값이 형성됩니다. 이 정보는 대차 대조표에서 토요일과 일요일에 해당하는 시간 간격이 노란색으로 표시될 수 있도록 사용됩니다.

다음으로 HistorySelect() 함수를 사용하여 지정된 간격의 거래 및 주문 내역을 확인할 수 있으며 HistoryDealsTotal() 함수를 호출하여 우리는 내역에서의 거래의 수를 결정합니다. 그런 다음 거래 수에 따라 거래 특성 계산에 필요한 통계를 수집하는 주기가 정렬되고 주기가 끝나면 값이 결정됩니다.

스크립트를 만들 때 우리의 임무는 MetaTrader 5에서 생성된 보고서에 따라 거래 특성의 의미를 보존하는 것이었습니다. 스크립트에 의해 계산된 특성은 터미널의 도움말 파일에 제공된 설명과 일치해야 한다고 가정합니다. 

계정 내역에 대한 액세스 및 거래 특성 계산에 대한 정보는 다음 문서에서 찾을 수 있습니다.

대부분의 특성은 매우 쉽게 계산되므로 이 글에서는 각 특성의 계산과 관련된 작업을 고려하지 않고 표준 보고서 및 부록과의 기존 차이점만 추가로 고려할 것입니다.

단말기에서 생성되는 보고서에서 대차대조표는 변경될 때마다 값이 순차적으로 표시되어 구성되며 X 스케일은 이러한 변경 횟수를 반영합니다. 우리의 경우 차트 구성을 위해 시간 척도를 사용합니다.

따라서 수익 차트는 터미널에서 생성된 차트와 매우 다릅니다. 실시간 규모로 포지션 마감 시간을 표시하기 위해 차트 구성의 이 옵션을 선택했습니다. 따라서 보고 기간 동안 거래 활동이 언제 증가하거나 감소했는지 알 수 있습니다.

차트를 구성할 때 MQL5는 1970년 1월 1일 이후 경과된 초 수로 표시된 날짜 값으로 작동하는 반면 그래픽 라이브러리는 이 값을 밀리초 수로 요구한다는 점을 염두에 두어야 합니다. 1970년 1월 1일부터 말이죠. 따라서 스크립트에 수신된 날짜 값이 올바르게 표시되려면 1000을 곱해야 합니다.

이익 가치와 거래 성사 시간을 저장하기 위해 스크립트는 표준 라이브러리CArrayDoubleCArrayLong 클래스를 사용합니다. 루프에서 결과 거래가 감지될 때마다 해당 정보가 Add() 메소드를 사용하여 배열 끝에 추가되는 요소에 배치됩니다. 이를 통해 필요한 요소 수를 미리 결정할 필요가 없습니다. 배열의 크기는 거래 내역에서 발견된 거래의 수에 따라 단순히 증가합니다.

각 거래에 대해 어떤 기호가 실행되었는지 확인하면서 기호 이름과 수행된 거래 수를 유지합니다. 이 데이터는 수익 차트와 마찬가지로 히스토리를 볼 때 배열 끝에 추가되는 요소에 기록하여 누적됩니다. 기호 이름과 거래 수를 저장하기 위해 표준 라이브러리의 CArrayStringCArrayInt 클래스를 사용합니다.

차트의 단일 열은 거래가 하나의 기호에 대해 실행된 경우, 너무 넓습니다. 이를 피하기 위해 데이터 배열에는 항상 최소 7개의 요소가 포함됩니다. 사용하지 않은 요소는 값이 0이므로 다이어그램에 표시되지 않으므로 열이 과하게 넓어지도록 하지 않습니다. 기호의 수가 적을 때 열이 대략 X축의 중간에 위치하도록 하기 위해 배열의 중요하지 않은 요소는 배열의 시작 또는 끝에 순차적으로 삽입됩니다.

표준 보고서와의 다음 차이점은 각 거래의 수익 값 시퀀스에 대한 확률 분포 차트를 구성하려는 시도입니다.

확률 밀도

그림 4. 확률 밀도

대부분의 경우 이러한 유형의 차트는 히스토그램 형식으로 표시됩니다. 우리의 경우 이러한 히스토그램의 기존 열 값을 기반으로 스플라인을 구성하여 확률 차트를 만듭니다. 계산된 확률 밀도 값은 차트 외부의 왼쪽과 오른쪽에서 0 값으로 보완됩니다. 이것은 스플라인 차트에 의해 구성된 마지막 알려진 값에서 중단되지 않고 차트를 넘어 계속되어 0으로 감소하기 위해 필요합니다.

비교를 위해 확률 밀도 차트에서 회색을 사용하여 정규 분포 차트를 강조 표시하고 차트와 마찬가지로 판독 값의 합이 1이 되도록 정규화합니다. 히스토그램의 값을 기반으로 구축되었습니다. 제공된 예시 보고서에서 거래 수는 이익 거래 가치의 확률 분포에 대한 다소 신뢰할 수 있는 추정치를 제공하기에 충분하지 않습니다. 내역상 많은 거래가 있을 때 이 차트가 더 확실해 보일 것이라고 가정할 수 있습니다.

모든 거래 특성이 계산되면 스크립트 끝에서 filesave() 함수가 호출됩니다. 이 함수는 변수의 이름과 값이 텍스트 형식으로 기록된 data.txt 파일을 엽니다. 이러한 변수의 값은 계산된 매개변수에 해당하고 이름은 그래픽 라이브러리의 기능으로 매개변수를 전송하는 동안 HTML 파일에서 사용되는 이름에 해당합니다.

파일을 쓰는 동안 디스크 접근 횟수를 줄이기 위해 짧은 줄을 하나의 긴 줄로 병합한 다음에만 파일에 기록합니다. MetaTrader 5에서 일반적으로 사용되는 data.txt 파일은 MQL5\Files 카탈로그에 생성됩니다. 이 파일이 이미 있으면 덮어씁니다. 편의를 위해 ProfitReport.htm 파일을 이 카탈로그에 복사하고 거기에서 실행할 수 있습니다.

MetaTrader 5 단말기에서 보고서를 HTML 형식으로 저장할 때 기본 브라우저로 등록된 브라우저에서 자동으로 열립니다. 이 가능성은 이 문서에 제공된 예제에서 구현되지 않았습니다.

자동 재생을 추가하려면 ProfitReport.mq5 스크립트 시작 부분에 다음 줄을 삽입하십시오.

#import "shell32.dll"
int ShellExecuteW(int hwnd,string lpOperation,string lpFile,string lpParameters,
                  string lpDirectory,int nShowCmd);
#import

그리고 결국, filesave() 함수를 호출한 후,

string path=TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\ProfitReport.htm";
ShellExecuteW(NULL,"open",path,NULL,NULL,1);

ProfitReport.htm 파일이 지정된 변수 경로에 존재하면 ShellExecuteW() 함수가 호출될 때 브라우저에서 열립니다. ShellExecuteW() 함수는 shell32.dll 시스템 라이브러리에 있으며 이 함수의 선언은 파일 시작 부분에 추가되어 이에 대한 액세스를 제공합니다.


결론

웹 브라우저를 사용하면 클라이언트 터미널에서 실행되는 Expert Advisor의 개별 모듈 내부 상태에 대한 시각적 제어 구성과 같은 다양한 정보를 동시에 표시할 수 있습니다.

자본 관리 데이터, 거래 신호, 후행 정지 및 기타 모듈 데이터를 동시에 편리하게 표시할 수 있습니다. 너무 많은 정보를 표시해야 하는 경우 다중 페이지 HTML 보고서를 사용할 수 있습니다.

JavaScript 언어의 기능은 차트를 그리는 것보다 훨씬 광범위하다는 점에 유의해야 합니다. 이 언어를 사용하여 진정한 대화형 웹 페이지를 만들 수 있습니다. 인터넷에서 웹 페이지에 포함된 준비된 JavaScript 코드와 이 언어 사용의 다양한 예를 찾을 수 있습니다.

예를 들어, 단말과 브라우저 간의 양방향 데이터 교환이 이루어지면 브라우저 창에서 직접 단말을 관리할 수 있습니다.

이 글에서 설명한 방법이 유용하기를 바랍니다.


파일

JS_Lib.zip            - highcharts.js 및 jquery.min.js
librariesTest.zip   - highcharts.js, jquery.min.js 및 Test_01.htm
Example1.zip       - highcharts.js, jquery.min.js, Example1.htm, Example1.mq5 및 exdat.txt
Example2.zip       - Example2.htm 및 exdat.txt
Report.zip           - ProfitReport.htm 및 data.txt
ProfitReport.mq5 - 통계 수집 및 data.txt 파일 생성을 위한 스크립트

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

파일 첨부됨 |
js_lib.zip (53.99 KB)
test.zip (54.53 KB)
example1.zip (55.31 KB)
example2.zip (54.3 KB)
report.zip (57.25 KB)
profitreport.mq5 (17.12 KB)
지그재그 및 ATR의 예에 의한 지표의 클래스 구현 지그재그 및 ATR의 예에 의한 지표의 클래스 구현
지표를 계산하는 최적의 방법에 대한 논쟁은 끝이 없습니다. 지표 자체에서 지표 값을 계산하거나 이를 사용하는 Expert Advisor에 전체 논리를 포함시켜야 하는 곳은 어디입니까? 이 글에서는 계산을 최적화하고 prev_calculated 값을 모델링하여 Expert Advisor 또는 스크립트의 코드에서 사용자 지정 지표 iCustom의 소스 코드를 바로 이동하는 변형 중 하나를 설명합니다.
거래 내역을 기반으로 한 거래 플레이어 거래 내역을 기반으로 한 거래 플레이어
트레이딩 플레이어. 설명이 필요없는 딱 네 단어. 버튼이 있는 작은 상자에 대한 생각이 떠오릅니다. 버튼 하나 누르기 - 재생, 레버 이동 - 재생 속도가 변경됩니다. 실제로는 꽤 비슷합니다. 이 글에서는 거의 실시간으로 트레이딩 히스토리를 재생하는 제 발전을 보여주고 싶습니다. 이 글에서는 지표 작업 및 차트 관리, OOP의 일부 뉘앙스를 다룹니다.
랜덤 워크와 추세 표시기 랜덤 워크와 추세 표시기
랜덤 워크는 실제 시장 데이터와 매우 유사해 보이지만 몇 가지 중요한 기능을 갖고 있습니다. 이 글에서는 동전 던지기 게임을 사용하여 시뮬레이션한 랜덤 워크의 속성을 고려할 것입니다. 데이터의 속성을 연구하기 위해 경향성 지표가 개발되었습니다.
움직이는 Mini-Max: MQL5의 기술적 분석 및 구현을 위한 새로운 지표 움직이는 Mini-Max: MQL5의 기술적 분석 및 구현을 위한 새로운 지표
다음 글에서는 Z.G.Silagadze의 논문 'Moving Mini-max: 기술 분석을 위한 새로운 지표'를 기반으로 Moving Mini-Max 지표를 구현하는 과정을 설명합니다. 지표의 아이디어는 알파 붕괴 이론에서 G. Gamov가 제안한 양자 터널링 현상의 시뮬레이션을 기반으로 합니다.