Создание индикатора Volumes

1. Сотворение шаблона.

- Запускаем редактор mCode любым способом: в меню справа есть соответствующая кнопка,

 а в меню Индикаторы - соответствующий пункт.

- В открывшемся окне редактора нажимаем кнопку Создать Индикатор.

- В открывшемся окне диалога задаём имя файла.

"Как вы лодку назовёте - так она и поплывёт" - в том смысле, что название файла и станет названием индикатора в списке пользовательских индикаторов торгового терминала.

Соответственно, имя индикатору даётся по-русски - если мы хотим видеть его в списке по-русски, и наоборот. Пробелы в имени файла редактор допускает, но их лучше избегать по соображениям совместимости операционных систем;

вместо пробела лучше использовать символ подчеркивания - после перезапуска терминала эти символы в имени индикатора в списке пользовательских индикаторов будут заменены на пробелы и читаемость не пострадает.

Ввели имя файла (расширение указывать не нужно) - нажимаем кнопку Добавить.

Имя файла появится в списке слева.

Слева от имени файла будет присутствовать символ - точка, пока серого цвета; он станет зеленым, когда мы активируем индикатор, но до этого созданный для нас шаблон нужно наполнить содержанием,

чтобы он стал действующим индикатором.

Стандартный шаблон, предлагаемый редактором на момент написания данного текста, выглядит так:

export class Main extends Indicator {
constructor() {
super();              
this.addInput("PriceType", InputType.PriceType, PriceType.Bid);
this.buffer = this.addBuffer(); 
}    
onInit() {   
}
async onUpdate() {
}
} 

Что это? - заготовка программы на языке JavaScript [developer.mozilla.org/ru/docs/Web/JavaScript], которую нам предстоит дополнить до полноценного индикатора.

Пойдём по порядку (порядок, впрочем условный, мы можем расставить функции иначе -  onUpdate, onInitconstructor; интерпретатор разберется, что к чему ): >export class Main extends Indicator Мы создаём и экспортируем класс с именем Main - дочерний класс (наследующий свойства) предопределенного класса Indicator. "Умом этого не понять, поэтому просто запомните", - сказано в анекдоте, и вполне можно относиться ко всей этой строчке именно так, она одинакова для всех пользовательских индикаторов, насколько мне известно. Но при желании понять можно - вот ссылки на соответствующую документацию JavaScript и mScript:

developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Statements/export

developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Statements/class

developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Classes/extends

mtrader6.com/docs/Terminal/mScript/JavaScript/v1/Indicator

mtrader6.com/docs/Terminal/mScript/JavaScript/v1/mScript

Наша шаблонная программа пока состоит из трёх частей: метода constructor и двух функций onInit и onUpdate, это минимальный набор, который может быть расширен при необходимости.

- constructor создаёт и инициализирует объекты, необходимые для работы индикатора developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Classes/constructor

Изначально constructor в шаблоне содержит три строки:

super();

developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/super

"В конструкторе ключевое слово super используется как функция, вызывающая родительский конструктор. Её необходимо вызвать до первого обращения к ключевому слову this в теле конструктора."

this.addInput("PriceType", InputType.PriceType, PriceType.Bid);

Добавим в начальное диалоговое окно запрос о типе цены.

this.buffer = this.addBuffer();

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/mScript/addBuffer

Создадим один буфер. Если понадобится, создадим их несколько. - onInit - стандартная функция инициализации индикатора, исполняется немедленно после загрузки индикатора клиентским терминалом                             

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/mScript/onInit

Эта функция пока не делает ничего - мы напишем там то, что считаем нужным.

- onUpdate - стандартная функция, исполняемая каждый раз при поступлении нового тика по символу, для которого рассчитывается индикатор: именно она рисует и перерисовывает элементы индикатора в окне терминала - так, как мы ей укажем.

Эта функция асинхронная - async:

developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Statements/async_function

дабы мы могли использовать в этой функции выражение await, "которое приостанавливает выполнение функции async и ожидает ответа от переданного Promise, затем возобновляя выполнение функции async и возвращая полученное значение."

Подробнее: developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Operators/await

Эта функция также пока не делает ничего - от нас зависит, чего мы хотим в итоге.

2. Дополняем шаблон до индикатора.

Создание индикатора рассмотрим на примере индикатора Volumes. Попутно рассмотрим возможности русификации оригинального индикатора.

Не забывайте чаще сохранять уже написанное.

Первую строку

export class Main extends Indicator {

оставляем без изменений.

Допишем constructor до нужного нам вида.     

constructor() {         
super();  
this.setProperty(Property.SeparateWindow, true); //  Данные индикатора отображаются в отдельном окне?  true = да;  false = нет, но это - значение по умолчанию, тогда эту строчку можно исключить     
                                                         
this.setProperty(Property.ShortName, "Объёмы"); //  Отображаемое имя индикатора = Объёмы  (Volumes в оригинале)              
this.setProperty(Property.Minimum, 0); // Зададим минимальное значение индикатора = 0 

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/mScript/setProperty                                 

this.addInput("Тип цены", InputType.PriceType, PriceType.Bid); // Входные данные индикатора - по умолчанию цена покупки, Bid;

Можно изменить в окне диалога инициализации индикатора. Русифицируем метку диалогового окна запроса о типе цены: "Тип цены".

Если нас в принципе устраивает, что  "Тип цены" = Bid (по умолчанию), то и эту строчку (запрос типа цены) можно исключить, дабы не морочить голову пользователю лишними вопросами.

Это не единственный возможный запрос, в других индикаторах мы можем запросить у пользователя другие данные для расчетов - например,

this.addInput("MA_Periods", InputType.int, 49);  
this.buffers = { // Нам понадобится не один, а два буфера для значений нашего индикатора - Up и Down 
ExtVolumesUpBuffer : this.addBuffer(), //буфер, в который заносятся данные в случае роста значений индикатора 
ExtVolumesDownBuffer : this.addBuffer(), //буфер, в который заносятся данные в случае падения значений индикатора

}; 
this.arr = { 
ExtVolumesBuffer: []  // массив сохраняемых значений объёмов 
} 
} 

Наполним нужным нам смыслом onInit()

onInit() { 

this.buffers.ExtVolumesUpBuffer //инициализация первого из буферов - буфера увеличения значения индикатора 

.setShape(Shape.Histogram)  // вид графика - Гистограмма;

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/Buffer/setShape

.setColor(Color.Green)      // цвет линии гистограммы для значения "Рост" (UP):

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/Buffer/setColor

.setLabel("Рост")       // наименование пункта меню в окне диалога запуска индикатора (в оригинале Up)

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/Buffer/setLabel

// толщина линии по умолчанию =2  (можно выбрать другую из ниспадающего меню в момент добавления индикатора в окно графика):

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/Buffer/setWidth

.setDigits(0)       // точность индикатора - количество знаков значений индикатора после десятичной точки;

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/Buffer/setDigits

.setDrawingStyle(3)         // в окне инициализации можно также задать стиль линии по умолчанию;

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/Buffer/setDrawingStyle

; 
this.buffers.ExtVolumesDownBuffer //инициализация второго буфера - буфера падения значения индикатора; аналогично первому буферу 
.setShape(Shape.Histogram)  // 
.setColor(Color.Red)        // цвет линии DOWN 
.setLabel("Падение")        // (в оригинале Down) 
.setWidth(2)                // 
.setDigits(0)               // 
  .setDrawingStyle(2)         // ;                                      
} // конец процедуры инициализации 

И наконец - собственно расчеты и организация отображения их результатов. 

async onUpdate() { // получить входные переменные: // получить ранее запрошенный тип цены в виде объекта 

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/mScript/getInputs 

 // const - константа:

developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Statements/const

const {PriceType} = this.getInputs(),     // получить накопленные данные буферов:

{ExtVolumesDownBuffer, ExtVolumesUpBuffer} = this.buffers,                                            

{ExtVolumesBuffer} = this.arr,    // запросить из терминала массив {значения Volume для каждого бара} и дождаться его заполнения:

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/Bar/load

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/Bar/Mode

{Volume} = await Bar.load([Bar.Mode.Volume], PriceType),
        
		// barSize = количеству элементов массива Volume

            barSize = Volume.length;

        // объявление переменных, действительных только в пределах данного блока программы
		let i, nLimit, vol,
            nCountedBars = Bar.counted(); // nCountedBars = количеству баров, не подвергшихся изменению с момента последнего запуска индикатора:

mtrader7.com/docs/Terminal/mScript/JavaScript/v1/Bar/counted


		// если nCountedBars больше нуля, уменьшить значение на 1

        if (nCountedBars > 0) {
            nCountedBars--;
        }		
		
        nLimit = barSize - nCountedBars; // предел количества итераций цикла
        
        // цикл по i от нуля до nLimit-1 по возрастанию i с шагом 1 - от последнего бара к началу графика

		for (i = 0; i < nLimit; i++) {
            vol = Volume[i]; // присвоить промежуточной переменной значение Volume очередного бара

            ExtVolumesBuffer[i] = vol;        // сохранить очередное значение Volume в соответствующем массиве индикатора

            if (i === barSize - 1 || vol > Volume[i + 1]) { // если это последнее доступное значение массива Volume или это значение Volume больше Volume предыдущего бара (слева, более раннего), то

                ExtVolumesUpBuffer.set(i, vol);   // отрисовать очередное (для бара с индексом i) значение гистограммы цветом буфера UP и высотой значения Volume

                ExtVolumesDownBuffer.set(i, 0);   // не рисовать ничего цветом DOWN
            } else { // в противном случае

                ExtVolumesUpBuffer.set(i, 0);   // отрисовать очередное (для бара с индексом i) значение гистограммы цветом буфера DOWN и высотой значения Volume

                ExtVolumesDownBuffer.set(i, vol);   // не рисовать ничего цветом UP
            } // конец условного оператора
        } // конец цикла
    } // конец процедуры onUpdate
}  

Теперь у нас есть всё, чтобы индикатор заработал. Нажимаем в редакторе mCode переключатель Добавить в "Мои индикаторы" (он станет зеленым ) и нажимаем Сохранить - зеленой станет и точка слева от имени индикатора.

Переходим в торговый терминал, выбираем график нужной валютной пары, и добавляем в него наш индикатор через меню Индикаторы - Пользовательский - Имя_Вашего_Индикатора.

Если всё сделано правильно - результат работы индикатора отобразится в окне графика.