Ваші недокументовані можливості при написанні радників на mQL4

Anonim

В журналі публікується безліч оглядів програм-радників і їх результативності: одні більш вдалі, інші відразу розцінюються як збиткові, що теж саме по собі результат. Але результативність радника, при його ретельному розгляді, це комплексний підсумок:

1) застосовуваної торгової стратегії (алгоритм вироблення сигналів відкриття-закриття);

2) застосовуваної техніки управління капіталом (алгоритм управління розміром лотів);

3) точності відповідності програми, записаної на MQL, описами двох перших пунктів (наявність логічних помилок) і відсутність в ній непередбачених побічних ефектів.

За двома першими позиціями написано багато, а зараз я хотів би зупинитися на деяких тонких питаннях використання мови MQL , які не знайшли ніякого відображення і згадки в описах мови (наприклад, в викладеної тут: _ // docs. mql4. com / ru / документації).

Це важливо з двох причин:

? по-перше, знання поведінки конструкцій мови, не відбите в документації, запобігає виникненню мало зрозумілих побічних ефектів в програмі;

?по-друге, дозволяє писати ефективніший код , менш схильний до прихованих помилок. Для використання цих можливостей в своїх радників не потрібно якоїсь особливої ​​кваліфікації, їх потрібно тільки назвати і знати, що таке можна використовувати. А якщо в наступному тексті якісь формулювання вам здадуться трохи ускладненими … безболісно пропускайте їх, вони зроблені тільки для повноти викладу.

Передача параметрів в функцію за посиланням (за адресою)

Ця річ прекрасно відома програмістам на будь-якому з універсальних мов програмування, наприклад, на C, а ще більше на C ++. Напишемо найпростіший радник (файл t2. Mq4 - тут і далі імена тестових прикладів в доданому архіві), і помістимо його на графік будь-якої валютної пари:

int start () {

static int nTick = 0;

Inc (nTick);

Alert («новий тик №» + nTick);

}

// -------------

void Inc (int n) {n ++;}

Результатом виконання буде безперервне повторення одного і того ж результату: «новий тик №0» … Передане в функцію значення n змінюється там, але це не робить ніякого впливу на зовнішнє оточення функції (що викликала функцію start ()) - в функцію передається копія змінної nTick, так зазвичай працює виклик функцій в MQL4. Додамо один символ (&) в опис функції Inc ():

void Inc (int & n) {n ++;}

Виконання радника радикально зміниться:

новий тик № 1

новий тик № 2

новий тик № 3 …

Значок &

вказав, що в функцію потрібно передати нам не копію, а оригінал змінної nTick, і всі зміни, зроблені над цієї змінної всередині функції, будуть відображатися на значенні цієї змінної зовні функції. Така поведінка називають побічний ефект функції .

Висновки:

Які наслідки несе в собі цей маленький курйоз? Дуже далекосяжні. Функції в MQL вельми обмежені, вони можуть повертати лише одне значення найпростішого типу: int, string, datetime, … За рахунок побічного ефекту, функція з багатьма параметрами може внести зміни в усі з них, фактично роблячи повернення (після свого виконання) більше одного результату (або це можна розцінювати як повернення структурованих даних). Наприклад, така функція:

double Func (int & n, string & s, datetime & t) {…}

Передача в функцію параметром масиву по посиланню

Наступний приклад (файл t3. Mq4) близький до попереднього, але тут побічні ефекти і супутні можливості істотно більше:

extern int N = 7;

int A [];

// ----------------------

int init () {

ArrayResize (A, N);

Alert («розмір масиву» + ArraySize (A));

for (int i = 0; i

}

// ----------------------

int start () {

static nBars = 0; // щоб не вчащати, тепер на барах, а не на тиках

if (nBars! = Bars) {// щоб не чекати, запускайте на M1

nBars = Bars;

IncArray (A);

ShowArray (A);

}

}

// ----------------------

void IncArray (int & Array []) {

int n = ArraySize (Array);

for (int i = 0; i

}

void ShowArray (int Array []) {

int n = ArraySize (Array);

string Msg = «»;

for (int i = 0; i

Msg = Msg + Array [i];

if (i

}

Alert (Msg);

}

Перенесіть радник на графік (краще тайм-фрейму M1) і ви будете мати задоволення спостерігати висновок подібних повідомлень:

2, 3, 4, 5, 6, 7, 8

3, 4, 5, 6, 7, 8, 9

4, 5, 6, 7, 8, 9, 10

Висновки:

Тепер змінам піддається окремо кожен елемент переданого масиву. Примітка:

Цікаво, як зміниться поведінка програми, якщо масив у функцію IncArray () буде передаватися не по посиланню?Чи буде створюватися копія масиву для роботи з ним всередині функції (подібно до попереднього прикладу для одиничного значення)? Досягаємо цього, переписавши заголовок цієї функції ось так (всього-то):

void IncArray (int Array []) {

Результат несподіваний навіть для бувалих програмістів на C або C ++! На етапі компіляції, не доходячи до виконання, ми отримаємо повідомлення про помилку виду:

'Array' - array item can not be assigned C: Program Files MetaTrader - Alpari experts t3. mq4 (22, 33)

Це означає, що контроль на L-value (доступність на присвоювання) для елементів масиву робиться ще компілятором! А для одиничних (скалярних) параметрів - це не так.

Зміна розмірності масиву, переданого за посиланням

Наступний трюк може повалити в подив навіть запеклого програміста на класичних мовах програмування. Сама по собі можливість в будь-який час змінити розмір масиву - це вже сама по собі значна особливість мови MQL4:

int A [5];

for (i = 0; i

ShowArray (A);

ArrayResize (A, 7);

for (i = 0; i

ShowArray (A);

Після виконання чого отримаємо, як не дивно: 333, 333, 333, 333, 333, 0, 0

Виходить, що масиву не переразмещает повністю, а «доповнюються» до потрібної розмірності - всі раніше присвоєні значення елементів зберігаються! Але без такого трюкацтва було б, мабуть, неможливо розробникам MQL4 реалізувати уявлення тайм-серії в MT4.

А тепер сам обіцяний трюк - зі зміною розмірності масиву всередині функції.

Для цього тільки перепишемо одну функцію з попереднього прикладу:

void IncArray (int & Array []) {

int n = ArraySize (Array);

ArrayResize (A, n + 1);

for (int i = 0; i <= n; i ++) Array [i] = i + 1;

}

Результат виконання (скопійовано з журналу терміналу MetaTrader 4 (// fortrader.org / forex-terminals / torgovyj-terminal-metatrader-4-skachat. html), який розгортає результати знизу-вгору):

2011 року. 02. 10 19: 54: 00 @ t3 EURUSD, M1: alert: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14

2011 року. 02. 10 19: 53: 02 @ t3 EURUSD, M1: alert: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13

2011 року. 02. 10 19: 52: 08 @ t3 EURUSD, M1: alert: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12

Примітка: Ще раз повернемося до питання, розглянутого вище: як взагалі (копією або оригіналом) передається в функцію масив в MQL4? Для цього ще раз перепишемо:

void IncArray (int Array []) {

int n = ArraySize (Array);

ArrayResize (A, n + 1);

}

І отримаємо досить несподіваний результат:

2011 року. 03. 07 20: 17: 02 t4 EURUSD, M1: Alert: 1, 2, 3, 4, 5, 6, 7, 0, 0, 0

2011 року. 03. 07 20: 16: 01 t4 EURUSD, M1: Alert: 1, 2, 3, 4, 5, 6, 7, 0, 0

2011 року. 03. 07 20: 15: 44 t4 EURUSD, M1: Alert: 1, 2, 3, 4, 5, 6, 7, 0

В результаті, можна стверджувати, що масив передається в функцію завжди по посиланню, але якщо ви це не вкажете явно (&),

компілятор

буде стежити і перешкоджати спробам змінити елементи масиву.