Особенности написания экспертов

FXWizard

Гуру форума
Особенности написания экспертов

Написание и тестирование экспертов в торговой системе MetaTrader имеет ряд особенностей.

Перед открытием позиции необходимо проверить, есть ли свободные деньги на счете. Если денег на счете недостаточно, то операция по открытию позиции закончится неудачно. При этом только при тестировании значение "FreeMargin" должно быть обязательно не меньше 1000, поскольку при тестировании цена одного лота составляет 1000.

if(AccountFreeMargin() < 1000) return(0); // денег нет - выходим

Доступ к историческим данным можно получить, используя индексированные предопределенные массивы Time, Open, Low, High, Close, Volume. Исторически сложилось так, что индекс в этих массивах растет от конца к началу. Т.е., самые последние данные имеют индекс 0. Индекс 1 означает данные со смещением один период назад, индекс 2 - два периода назад, 3 - три периода назад и т.д..

// если Close на прошлом баре меньше, чем
// Close на позапрошлом баре
if(Close[1] < Close[2]) return(0);

Возможна также организация доступа к историческим данным по другим временным интервалам и даже по другим валютным парам. Для получения таких данных следует предварительно определить одномерный массив и выполнить операцию копирования с помощью функции "ArrayCopySeries". Причем при вызове функции можно передавать меньшее количество параметров и не указывать параметры по умолчанию.

double eur_close_m1[];
int number_copied = ArrayCopySeries(eur_close_m1, MODE_CLOSE,
"EURUSD", PERIOD_M1);


При написании эксперта так же, как и при создании любой другой программы, бывает необходим вывод некоторой дополнительной отладочной информации. Язык MQL 4 предоставляет несколько возможностей для вывода такой информации.

Функция "Alert" выводит на экран диалоговое окно, содержащее определённые пользователем данные.

Alert("FreeMargin grows to ", AccountFreeMargin(), "!");

Функция "Comment" выводит в левый верхний угол графика определённые пользователем данные. Символьная последовательность "\n" используется для перевода строки.

Comment("FreeMargin is ", AccountFreeMargin(), ".");

Функция "Print" печатает определённые пользователем данные в системный журнал.

Print("FreeMargin is ", AccountFreeMargin(), ".");

Для получения информации об ошибках в программах очень полезной является функция "GetLastError". Например, операция с ордером всегда возвращает номер тикета. Если номер тикета равен 0 (возникла какая-то ошибка при выполнении операции), то для определения дополнительной информации об ошибке необходимо вызвать функцию "GetLastError":

int iTickNum = 0;
int iLastError = 0;

...

iTickNum = OrderSet (OP_BUY, g_Lots, Ask, 3, 0, Ask
+ g_TakeProfit * g_Points, Red);

if (iTickNum <= 0)
{
iLastError = GetLastError();
if (iLastError != ERROR_SUCCESS) Alert("Some Message");
}


Следует помнить, что вызов функции "GetLastError" выдает код последней ошибки и обнуляет ее значение. Поэтому повторный последовательный вызов этой функции всегда будет возвращать значение 0.

Как определить начало очередного бара? (Это бывает необходимо, чтобы узнать, что предыдущий бар только что сформировался.) Существует несколько способов.

Первый способ основан на проверке количества баров:

static int prevbars = 0;
...

if(prevbars == Bars) return(0);
prevbars = Bars;

...

Этот способ может не сработать при подкачке истории. То есть, количество баров изменилось, а "предыдущий" еще не сформировался. В этом случае можно усложнить проверку на разницу между значениями, равную единице.

Следующий способ основан на том, что значение "Volume" формируется на основе количества тиков, пришедших для каждого бара, и первый тик означает, что у вновь формирующегося бара значение "Volume" равно 1:

if( Volume[0] > 1) return(0);
...

Этот способ может не сработать при слишком интенсивном поступлении ценовых тиков. Дело в том, что обработка приходящих ценовых тиков производится в отдельном потоке. И если этот поток занят во время поступления очередного тика, то во избежание излишней загрузки вычислительных ресурсов этот пришедший тик не обрабатывается! В этом случае можно также усложнить проверку, используя сохранение предыдущего значения "Volume".

Третий способ основан на времени открытия бара:

static datetime prevtime=0;
...
if(prevtime == Time[0]) return(0);

prevtime = Time[0];

...

Это - самый надежный способ. Он сработает при любых обстоятельствах.

Пример работы с файлом типа "CSV":

int h1;

h1 = FileOpen("my_data.csv", MODE_CSV | MODE_WRITE, ";");

if(h1 < 0)
{
Print("Unable to open file my_data.csv");
return(false);
}

FileWrite(h1, High[1], Low[1], Close[1], Volume[1]);

FileClose(h1);


Некоторые пояснения к коду. Сначала открывается файл формата "CSV". В случае ошибки открытия файла происходит выход из программы. В случае успешного открытия файла производится стирание его содержимого, запись данных в файл и последующее закрытие файла. Если необходимо сохранить содержимое открываемого файла, то следует использовать режим открытия MODE_READ:

int h1;
h1 = FileOpen("my_data.csv", MODE_CSV | MODE_WRITE | MODE_READ, ";");

if(h1 < 0)
{
Print("Unable to open file my_data.csv");
return(false);
}

FileSeek(h1, 0, SEEK_END);
FileWrite(h1, High[1], Low[1], Close[1], Volume[1]);

FileClose(h1);


В этом примере запись производится в конец файла. Для этого сразу после его открытия мы воспользовались функцией "FileSeek".
 
Верх