
Графики и диаграммы в формате HTML
Введение
Скорее всего MetaTrader 5 является вполне самодостаточным продуктом и в дополнительных расширениях не нуждается. MetaTrader 5 обеспечивает связь с брокером, отображает котировки, позволяет использовать для анализа рынка разнообразные индикаторы и, конечно, предоставляет трейдеру возможность осуществлять торговые операции. Совершенно ясно, что ориентированный в первую очередь на предоставление возможности комфортно торговать, MetaTrader 5 не может, да, наверное, и не должен являться абсолютно универсальным инструментом, предназначенным для научных исследований, анализа математических методов, создания мультимедийного контента и так далее.
Более того, стремление к излишней универсальность программного продукта в конечном итоге чаще всего приводит к снижению его эффективности, надежности и безопасности. С другой стороны, в той или иной ситуации у пользователя может возникнуть потребность в каких-то дополнительных возможностях, тем более что трейдерами являются люди, имеющие различные специальности и разное образование. Поэтому любые дополнительные возможности могут увеличить привлекательность торговой платформы, если, конечно, они достигаются достаточно простым способом и не за счет снижения ее надежности и безопасности.
В данной статье рассмотрим один из вариантов такого дополнения, который может дать возможность строить на основе полученных в терминале различные графики и диаграммы.
Каждая программа должна делать то, что у нее лучше всего получается. Если придерживаться такого принципа, то давайте возложим на MetaTrader 5 обязанности обмена с брокером, сбор и обработку полученной информации, а для графического отображения этой информации будем использовать другую, предназначенную для этих целей программу.
WEB-браузер
Сегодня трудно найти компьютер, на котором не был бы установлен WEB-браузер. Браузеры уже на протяжении длительного времени постоянно развиваются и совершенствуются. Современные браузеры достаточно надежны, стабильны в работе и, что немаловажно, бесплатны. Учитывая, что WEB-браузер является практически основным инструментом служащим для доступа в сеть Internet, подавляющее большинство пользователей хорошо знакомы с ним и не испытывают ни каких трудностей при его использовании.
Возможности современных браузеров достаточно широки, мы привыкли к тому, что с помощью WEB-браузера просматриваем видеоролики, слушаем музыку, играем в игры и уже совершенно не обращаем на это внимания. Таким образом, WEB-браузер на сегодняшний день является достаточно развитым инструментом для отображения различного рода информации, которая может быть представлена в совершенно разных форматах.
Нельзя не упомянуть тот факт, что в настоящее время существует несколько популярных, хорошо себя зарекомендовавших WEB-браузеров, среди них InternetExplorer, Mozilla Firefox, Google Chrome, Opera. В части программной реализации и пользовательского интерфейса эти браузеры могут существенно отличаться друг от друга. Но при этом, теоретически, должны в полной мере поддерживать основные стандарты, принятые в сети для обмена информацией, что в первую очередь касается непосредственно стандартов языка HTML.
На практике, несмотря на усилия разработчиков, браузеры все же имеют некоторые индивидуальные особенности в плане реализации тех или иных протоколов или технологий. Если мы сталкиваемся с тем, что тот или иной браузер благодаря своим индивидуальным особенностям нас не устраивает, то эту проблему легко решить установкой на компьютер дополнительно одного или нескольких разных WEB-браузеров. Даже ярые приверженцы, например, Firefox, все равно кроме него имеют в системе как минимум Internet Explorer.
Несмотря на то, что WEB-браузеры разрабатывались как клиентская часть, обеспечивающая взаимодействие с удаленным сервером, они также могут использоваться и для отображения локальной информации, размещенной на компьютере пользователя. Примером тому может быть просмотр ранее сохраненных на компьютере WEB-страниц. При таком локальном режиме работы доступ в сеть браузеру может вовсе не понадобиться.
Таким образом, WEB-браузер, работающий в offline-режиме, является очень привлекательным кандидатом на роль программы, используемой для расширения графических возможностей терминала MetaTrader 5. Для его использования не потребуются ни дорогостоящие покупки, ни громоздкие длительные инсталляции, ни освоение пользователем нового программного продукта. Поэтому далее в статье будем рассматривать возможность использования именно WEB-браузера для построения графиков и диаграмм на основе данных полученных в MetaTrader 5.
HTML и JavaScript
Остановив свой выбор на использовании в качестве расширения WEB-браузера, определим для себя основное правило, которого в дальнейшем будем строго придерживаться - отображение созданных нами HTML-страниц должно осуществляться без участия локального или удаленного WEB-сервера. То есть мы не будем устанавливать на нашем компьютере серверное программное обеспечение, и отображение наших страниц не потребует наличия выхода в сеть. Созданные нами HTML-страницы должны отображаться только средствами самого WEB-браузера и должны быть размещены на нашем компьютере. Это правило позволит свести к минимуму риск, связанный с возможным снижением безопасности за счет выхода во внешние сети.
Используя для вывода информации только возможности HTML 4, можно создавать WEB-страницы с таблицами и форматированным текстом, размещать на странице изображения, но эти возможности нас не могут полностью удовлетворить, так как нашей целью является построение полноценных графиков и диаграмм на основе данных полученных от MetaTrader 5.
В большинстве случаев, то, что мы видим в браузере, путешествуя по различным сайтам, создано при помощи расширений языка HTML. В основном эти расширения исполняются на стороне сервера и по этой причине нам совершенно не подходят. Для нас могут представлять интерес технологии, которые в состоянии работать на стороне браузера и не требуют серверного программного обеспечения, например, Macromedia Flash, JavaScript, Java.
Если для исполнения на стороне браузера приложений Macromedia Flash и Java нам понадобится как минимум установка дополнительных плагинов, то пользовательские программы, написанные на JavaScript, выполняются непосредственно самим браузером. Все распространенные WEB-браузеры имеют собственный встроенный JavaScript интерпретатор. Для того чтобы избежать необходимости в инсталляции каких либо дополнительных программ или плагинов,остановим свой выбор на JavaScript.
Таким образом, в дальнейшем будем использовать только MetaTrader 5 с MQL5 и WEB-браузер с HTML и JavaScript, никакого дополнительного программного обеспечения не понадобится. Следует напомнить, что HTML-страница является ни чем иным, как текстовым файлом. Поэтому для создания документа в формате HTML подойдет любой текстовый редактор. Можно, например, создавать и редактировать HTML-код в том же MetaEditor 5. При написании статьи редактирование HTML-кода осуществлялось в браузере Opera@USB v10.63, который позволяет редактировать содержимое страницы, сохранять измененную страницу и видеть в соседней закладке как она будет отображаться.
У человека не знакомого с языками HTML и JavaScript может резонно возникнуть опасение в связи с возможными трудностями, связанными с их освоением. Для того что бы максимально облегчить себе задачу и избежать углубленного изучения HTML и JavaScript, попробуем использовать готовые решения, базирующиеся на этой технологии. Так как в рамках данной статьи наша задача ограничивается только построением графиков и диаграмм, обратимся к готовым, специально для этого написанным JavaScript-библиотекам.
Достаточно развитой графической библиотекой является Emprise JavaScript Charts, возможно читателю будет интересно познакомиться с ней по приведенной ссылке, но данная библиотека не совсем бесплатная. Поэтому обратимся к бесплатным библиотекам, например, Dygraphs JavaScript Visualization Library и Highcharts charting library. Dygraphs привлекает своей компактностью и простотой, а библиотека Highcharts в свою очередь включает в себя больше возможностей и выглядит более универсальной. Несмотря на то, что библиотека Highcharts имеет размер примерно 75 KB и дополнительно требует наличия библиотеки jQuery, которая имеет размер примерно 70 KB, все же остановим свой выбор именно на ней.
Познакомиться с возможностями библиотеки Highcharts можно на сайте http://www.highcharts.com/ в разделе “Demo Gallery”. Для каждого из примеров выбрав пункт “View options” можно посмотреть его исходный JavaScript-код. Подробная документация по библиотеке находится в разделе “Documentation/Options Reference”, в этом разделе так же можно найти большое количество примеров использования той или иной опции. На первый взгляд из-за обилия JavaScript-кода и непривычного для 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>
Код примера для наглядности разделен комментариями на четыре части. Самая первая, верхняя часть кода содержит обычные для 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-файл, то есть из текущего каталога. Для того чтобы библиотеки были загружены браузером их необходимо предварительно скачать с http://ajax.googleapis.com/ и http://www.highcharts.com/ соответственно и скопировать в тот же каталог, где располагается наш HTML-файл. Обе эти библиотеки также можно найти в конце статьи в разделе “Файлы”.
В следующей части кода создается объект класса Highcharts.Chart. Параметр “renderTo: 'container1'“ указывает на то, что график будет выводиться в HTML-элемент с именем “container1”, а параметр “data” определяет данные, которые должны быть отображены на графике. Как видим в этом примере данные определяются непосредственно как параметры при создании объекта класса Highcharts.Chart. Путем несложных изменений вынесем определение отображаемых данных в отдельную часть кода, это позволит при необходимости отображения нескольких графиков сгруппировать их данные.
В последней части нашего примера тегом <div> объявляется HTML-элемент с именем “container1” и указываются размеры этого элемента. Как уже упоминалось ранее, именно этот 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. Необходимо помнить, что для нормального отображения этой тестовой страницы WEB-браузеру должно быть разрешено исполнять JavaScript. Так как браузеры в целях безопасности позволяют отключать эту опцию, она может оказаться выключенной. В результате мы должны увидеть следующее
Рисунок 1. Test_01.htm
Это наш первый тестовый график и, несмотря на кажущуюся сложность данной технологии, его создание не заняло много времени. Следует обратить внимание на некоторые особенности отображения графиков созданных таким способом. Откройте в скопированном каталоге файл Test_01.htm, и если WEB-браузер позволяет увеличивать и уменьшать просматриваемые страницы, то вы сможете заметить, что даже при существенном увеличении качество отображения графика не ухудшается.
Это связано с тем, что такой график не является статическим изображением, как, например, PNG или JPEG-файлы, а вычерчивается заново после увеличения или уменьшения области отведенной для его прорисовки. Поэтому такое изображение нельзя сохранить на диск привычным способом, так как мы обычно сохраняем понравившиеся нам картинки. Учитывая, что график был построен средствами JavaScript, нельзя не упомянуть о том, что разные браузеры, имея собственные встроенные интерпретаторы этого языка, могут не всегда одинаково его исполнять.
Графики, построенные с помощью JavaScript, в разных браузерах иногда могут отображаться с некоторыми незначительными отличиями. Чаще всего эти отличия по сравнению с другими браузерами могут проявляться у Internet Explorer.
Но будем надеяться на то, что создатели JavaScript-библиотек позаботятся о максимально возможной совместимости их кода с большинством распространенных WEB-браузеров.
MetaTrader 5 и MQL5
В приведенном выше тестовом примере данные предназначенные для вывода на график задавались вручную, при создании HTML-страницы. Для организации передачи данных из MetaTrader 5 в создаваемый нами график воспользуемся простейшим способом. Пусть MetaTrader 5 будет записывать данные в отдельный файл, из которого они будут загружаться в браузер при отображении графика. Напишем пример, включающий в себя HTML-страницу, которая будет отображать график, загружая данные из файла и скрипт на MQL5, который будет создавать этот файл.
В качестве 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-страницы должен будет подгрузить текстовый файл exdat.txt, в котором и будет производиться присвоение массиву dat1 значений предназначенных для отображения на графике. Этот файл должен содержать в себе просто фрагмент JavaScript-кода. Такой файл легко может быть создан в MetaTreder 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); } //+------------------------------------------------------------------+
В этом скрипте для хранения отображаемых данных используется массив gr[] размером 25 элементов. Это массив, в качестве примера, заполняется отсчетами синусоиды, при этом производится округление до четырех знаков после запятой. Массив, конечно, может быть заполнен любыми другими, более полезными данными.
Далее эти данные форматируются и объединяются в одну текстовую строку. Для того чтобы сократить объем создаваемого текстового файла, в строку выводятся значения элементов массива gr[] только с четырьмя знаками после запятой. Для этого используется функция DoubleToString(). После того как текстовая строка str сформирована она сохраняется в файле exdat.txt. В случае успешного исполнения скрипта, текстовый файл exdat.txt будет создан в каталоге клиентского терминала в подкаталоге \MQL5\Files; если такой файл уже существует, то он будет перезаписан.
Файлы jquery.min.js, highcharts.js, Example1.mq5, Example1.htm и exdat.txt представлены в конце статьи в разделе “Файлы”. Эти пять файлов размещены в каталоге \Example1. Для того что бы просто посмотреть на полученный результат, достаточно скопировать данный пример и двойным щелчком в каталоге \Example1 открыть файл Example1.htm. График будет построен по данным из файла exdat.txt.
Рисунок 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>
на
<script type="text/javascript"> </script>
Далее текстовым редактором, например, "Блокнот", откроем файл библиотеки jquery.min.js и, выбрав команду "Выделить все", скопируем содержимое файла. Далее откроем файл Example1.htm, вставим скопированный текст библиотеки между тегами <script type="text/javascript"> и </script>. Сохраним полученный файл под именем Example2.htm. В этот файл таким же образом скопируем содержимое библиотеки highcharts.js, поместив его между текстом ранее скопированной библиотеки и тегом </script>.
В результате копирования HTML-файл увеличился в размерах, но зато теперь для его корректного отображения не требуется наличия отдельных файлов библиотек. Достаточно в том же каталоге иметь только файл с данными exdat.txt. Каталог \Example2, включающий в себя файлы Example2.htm и exdat.txt находятся в конце статьи в разделе “Файлы”.
Отчет об истории торгового счета в графическом виде
Для более полной демонстрации предложенного способа отображения графической информации создадим отчет отображающий историю торгового счета на заданном интервале времени. В качестве прототипа будет служить HTML-отчет, который создается в терминале MetaTrader 5 при выборе команды "Отчет" контекстного меню вкладки "История". Этот отчет включает в себя большое количество различных показателей сведенных в одну таблицу. Рассчитывая на то, что эти показатели, представленные в виде графиков и диаграмм, будут выглядеть более наглядно, отобразим их при помощи графической библиотеки highcharts.js.
В приведенных ранее примерах при построении графика использовались установки отображения принятые по умолчанию в данной версии библиотеки highcharts.js. Для практических целей обойтись таким вариантом не удастся, в каждом случае придется подстраивать вид графика под индивидуальные конкретные требования. Для этого библиотека highcharts.js предоставляет широкие возможности, располагая большим количеством опций, которые могут быть применены к графику или диаграмме. Как уже упоминалось, список этих опций с их достаточно подробным описанием и примерами можно найти на сайте http://www.highcharts.com/.
Учитывая, что данная статья призвана только предложить и продемонстрировать возможность привлечения WEB-браузера для отображения информации полученной от MetaTrader 5, не будем останавливаться на описании опций графической библиотеки и особенностях их использования. Тем более что в зависимости от конкретных требований при создании WEB-страницы может быть использована какая-нибудь другая JavaScript-библиотека. Заинтересованный читатель может самостоятельно выбрать для себя наиболее подходящую ему библиотеку и углубиться в ее изучение настолько, насколько этого потребует практика ее использования.
Для отображения истории торгового счета был создан файл ProfitReport.htm, он находится в конце статьи в разделе “Файлы”. Вместе с ним в каталоге \Report находится файл data.txt, который содержит отображаемые данные. Файл data.txt помещен в каталог только в качестве примера. Скопировав каталог \Report и открыв файл ProfitReport.htm мы увидим представленные в графическом виде показатели торговли на созданном для данного примера тестовом счете.
Рисунок 3. ProfitReport.htm
При создании файла ProfitReport.htm сначала была произведена примерная разметка страницы, и было приблизительно определено, где и какая информация будет располагаться. Далее на странице были размещены графики с опциями принятыми по умолчанию. После создания такого шаблона индивидуально для каждого из графиков были подобраны наиболее подходящие опции. После завершения редактирования в страницу простым копированием были включены тексты библиотек. Как уже говорилось, для нормального отображения страницы она должна быть размещена в том же каталоге, что и файл data.txt содержащий предназначенные для отображения данные.
Файл data.txt был создан в MetaTrader 5 при помощи скрипта ProfitReport.mq5. В случае успешного исполнения этого скрипта в каталоге \MQL5\Files создается файл data.txt содержащий значения показателей торговли для активного в данный момент счета. Не следует забывать, что скрипт для его использования должен быть помещен в каталог \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 (без учета пополнения счета) int days_num; // количество дней в отчете (включая текущий) 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; // количество дней в отчете (включая текущий) 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() проверяется, подключен ли терминал к торговому серверу, если нет, то скрипт завершает свою работу. Так как при отсутствии подключения к серверу невозможно будет определить активный счет и получить информацию о нем.
Следующим шагом вычисляется дата, начиная с которой данные о торговле на текущем активном счете будут включены в отчет. В качестве конечной даты принимается значение текущей даты и текущего времени на момент исполнения скрипта. Количество дней, включенных в отчет можно задать при загрузке скрипта изменив значение входного параметра “Number of days”, равного по умолчанию 30 дням. После того как определено время начала и окончания отчета в строковой переменной Band формируются пары значений времени, соответствующие началу и окончанию выходных дней. Эта информация используется для того, чтобы на графике баланса интервалы времени, соответствующие субботе и воскресению, можно было пометить желтым цветом.
Далее при помощи функции HistorySelect() делается доступной история сделок и ордеров за указанный интервал и вызовом функции HistoryDealsTotal() определяется количество сделок в истории. После этого по количеству сделок организован цикл, в котором собирается статистика, необходимая для вычисления показателей торговли, и по окончанию цикла определяются их значения.
При создании скрипта ставилась задача сохранить смысл показателей торговли в соответствии с отчетом, генерируемым в терминале MetaTrader 5. Предполагается, что показатели, определяемые скриптом, должны соответствовать описанию, которое приведено в файле справки к терминалу.
Информацию по организации доступа к истории счета и вычислению торговых показателей можно найти в следующих статьях:
- "Ордерa, позиции и сделки в MetaTrader 5",
- "Создание информационных табло с использованием классов из Стандартной библиотеки и Google Chart API",
- "Что означают цифры в отчёте тестирования эксперта",
- "Математика в трейдинге. Оценка результатов торговых сделок".
Большинство показателей вычисляются достаточно просто, поэтому в данной статье не будем рассматривать операции, связанные с вычислением каждого из них, и в дальнейшем рассмотрим только имеющиеся отличия от стандартного отчета и дополнения к нему.
В отчете, который генерируется терминалом, график баланса строится последовательным отображением значений при каждом его изменении, а на шкале Х отражено количество таких изменений. В нашем случае для построения графика используется шкала времени.
Поэтому построенный нами график профита на вид очень сильно отличается от графика, который генерируется терминалом. Выбор именно такого варианта построения графика был сделан для того, чтобы отобразить моменты закрытия позиций на реальной шкале времени. То есть чтобы было видно, когда на протяжении отчетного периода возрастала или спадала торговая активность.
При построении графика необходимо помнить, что MQL5 оперирует значением даты, представленном в виде количества секунд, прошедших с 01 января 1970 года, а графической библиотеке требуется это значение в виде количества миллисекунд, прошедших с 01 января 1970 года. Поэтому полученные в скрипте значения даты, для их корректного отображения на графике, необходимо умножать на тысячу.
Для хранения значения профита и времени закрытия сделки в скрипте используются классы CArrayDouble и CArrayLong из состава стандартной библиотеки. Каждый раз при обнаружении в теле цикла результативной сделки, информация о ней помещается при помощи метода Add() в элемент, добавляемый в конец массива. Это позволило не определять заранее требуемое количество элементов, размер массива просто увеличивается с увеличением количества обнаруженных в истории сделок.
Для каждой сделки проверяется, на каком символе она была осуществлена, при этом сохраняется имя символа и количество совершенных на нем сделок. Так же как и для графика профита, эти данные при просмотре истории накапливаются путем записи в добавляемый в конец массива элемент. Для хранения имени символа и количества сделок используются классы CArrayString и CArrayInt стандартной библиотеки.
Если на текущем счете сделки осуществлялись только по одному символу, то отображаемый на диаграмме единственный столбец будет слишком широким. Что бы избежать этого передаваемый для отображения массив данных всегда содержит не менее 7 элементов. Неиспользуемые элементы на диаграмме не отображаются, так как они содержат нулевые значения, но при этом не позволяют столбцу стать слишком широким. Для того что бы при малом количестве символов столбцы диаграммы располагались примерно посередине оси X, незначащие элементы массива поочередно вставляются то в его начало, то в конец.
Следующим отличием от стандартного отчета является попытка построения графика распределения вероятностей для последовательности значений профита по каждой из сделок.
Рисунок 4. Плотность вероятности
Чаще всего такой график бывает представлен в виде гистограммы выборки. В нашем случае график плотности вероятности создается путем построения сплайна по имеющимся значениям столбцов такой гистограммы. Вычисленные значения оценки плотности вероятности дополняются слева и справа за пределами графика нулевыми значениями. Это необходимо для того что бы построенный сплайном график не прерывался на последнем известном значении, а продолжался за пределы графика спадая к нулю.
Для сравнения на графике плотности вероятности серым цветом выводится график нормального распределения, нормированный таким образом, что бы сумма представляющих его отсчетов была равна единице, так же как и у графика, построенного по значениям гистограммы. В приведенном примере отчета количества сделок явно недостаточно для более или менее достоверной оценки распределения вероятностей значений профита сделок. Можно предположить, что при большем количестве сделок в истории этот график будет выглядеть более достоверным.
После того, как все показатели торговли определены, в конце скрипта вызывается функция filesave(). В этой функции открывается на запись файл data.txt и в него записываются в текстовом виде имена переменных и их значения. Значения этих переменных соответствуют вычисленным показателям, а их имена соответствуют именам, которые используются в HTML-файле при передаче параметров функциям графической библиотеки. Для того, что бы при записи файла сократить количество обращений к диску, короткие строки объединяются в одну более длинную строку, и только потом происходит ее запись в файл. Файл data.txt, как это принято в MetaTrader 5, создается в каталоге 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);
Если по указанному в переменной path пути существует файл ProfitReport.htm, то при вызове функции ShellExecuteW() он будет открыт браузером. Функция ShellExecuteW() находится в системной библиотеке shell32.dll, поэтому для обеспечения доступа к ней в начале файла добавлено объявление этой функции. Для того чтобы такой автозапуск срабатывал необходимо разрешить в терминале импорт функций из внешних dll, так как по умолчанию использование внешних библиотек запрещено.
Заключение
Использование WEB-браузера позволяет отображать на экране одновременно достаточно много разнообразной информации, что может оказаться полезным, например, для организации визуального контроля за внутренним состоянием отдельных модулей работающего в терминале эксперта. На экране в удобной форме может быть одновременно представлена информация о состоянии модуля управления капиталом, модулей генерирующих торговые сигналы, модуля сопровождения торговой позиции и так далее. В случае, когда информации, которую необходимо отобразить, оказывается слишком много, можно использовать многостраничные HTML-отчеты.
Следует отметить, что возможности языка JavaScript значительно шире, чем просто рисование графиков, с его помощью отображаемую WEB-страницу можно сделать по-настоящему интерактивной. В Интернете можно найти большое количество включаемых в WEB-страницу готовых JavaScript-кодов и различных примеров использования этого языка. Например, если при помощи JavaScript между терминалом и браузером удастся организовать двухсторонний обмен данными, то можно будет непосредственно из окна браузера подавать команды терминалу.
Хочется надеяться, что рассмотренный в статье способ отображения информации будет востребован и окажется полезным.
Файлы
JS_Lib.zip - файлы библиотек highcharts.js и jquery.min.js
Test.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





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Вот код data-from-csv.htm
А вот файл данных data.csv:
Файлы библиотек не прикладываю.
Спасибо за "помощь": как обычно, никто не помог, сам разобрался :-)
Вопрос к автору, а есть ли что-то подобное для 3D графиков?
Да уж. Графическое представление информации в МТ5 слабовато. А сколько всякого интересного можно нарисовать!: графики корреляций всего и вся, динамическое отображение текущего курса через разные вейвлет преобразования, 3D и чёрт знает что ещё. Так и хочется залепенить какой-нить яркий скрин сейвер по полухаотичным корреляциям остатков рядов разных курсов!
Особенно плохо с графикой в тестере стратегий. Даже экспорт в текстовый файл результатов тестера толком не сделан.
Кстати, раз уж всё равно экспортировать данные в файл, то лучше потом не изобретать свою графику, а воспользоваться готовыми пакетами винниграфикс, сигмаплот, ориджин или эксель.
Особенно порадовал венец статьи - функция рапределения вероятностей. Тянется народ к разумному исследованию курса. Это радует.
Не могу понять где можно скачать файлы, которые по идее приложены к статье? Названия файлов есть, а ссылок в них нет:
Файлы
JS_Lib.zip - файлы библиотек highcharts.js и jquery.min.js
Test.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