» Календарь |
« Май 2024 » | Пн | Вт | Ср | Чт | Пт | Сб | Вс | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
|
|
Торговые советники (Expert Advisors)- Читать Закрыть 41
Выдерживаем паузу между сделками.
В клиентском терминале MetaTrader 4, в отличии от MetaTrader 3, нет
жесткого ограничения на паузу между торговыми операциями, совершаемых
советниками. Поэтому теоретически можно совершать хоть десятки сделок в
секунду. Однако чрезмерное злоупотребление этим приведет к тому, что Ваш
счет будет заблокирован для торговли.
Это произойдет или потому, что сервер подумает, что такое количество
запросов в секунду может осуществлять только злоумышленник, который
ставит перед собой лишь цель «завалить» торговый сервер. Или потому, что
сотнями запросами в минуту Вы разъярите дилера — сотрудника дилингового
центра, которому приходится обрабатывать Ваши запросы.
Считается правилом хорошего тона не совершать торговые операции чаще,
чем 1 раз в 5-10 секунд. Конечно, в любом правиле могут быть исключения
и иногда Вам крайне необходимо совершить операции с меньшим временным
лагом, но старайтесь этой возможностью не злоупотреблять.
Старайтесь выдерживать паузу минимум в 5 секунд. В этом Вам поможет моя функция WaitBeforeTransaction()
//+-------------------------------------------------------------------+
//| Фунцкия WaitBeforeTransaction выдерживает паузу Secs секунд
//| между торговыми операциями эксперта (по умолчанию 5 секунд)
//|
//| Возвращает:
//| 1 - если пауза выдержена без ошибок
//| 0 - если эксперт был остановлен
//| -1 - если произошла какая-то ошибка
//+--------------------------------------------------------------------+
int WaitBeforeTransaction(int Secs = 5)
{
// если режим тестирования, то ждать необязательно
if (IsTesting()) return(1);
// если глобальная переменная LastTradeTime не существует,
// то создать ее
if (!GlobalVariableCheck("LastTradeTime"))
{
// Если произошла какя-то ошибка при вызове функции
// GlobalVariableCheck(), выйдем с ошибкой
if (GetLastError()!=0)
{
Print("WaitBeforeTransaction(): ошибка ",GetLastError(),
" при проверке глобальной переменной LastTradeTime");
return(-1);
}
// глобальная переменная не существует, создадим ее
if (GlobalVariableSet("LastTradeTime", 1)==0)
{
// произошла ошибка при создании глобальной переменной
Print("WaitBeforeTransaction(): ошибка ",GetLastError(),
" при создании глобальной переменной LastTradeTime");
return(-1);
}
// глобальная переменная успешно создана
Print("WaitBeforeTransaction(): глобальная переменная ",
"LastTradeTime создана");
}
// получим время последней операции
datetime LastTradeTime;
LastTradeTime = GlobalVariableGet("LastTradeTime");
// если произошла ошибка (равна нулю), то выходим с ошибкой
if (LastTradeTime==0)
{
Print("WaitBeforeTransaction(): ошибка ",GetLastError(),
" при чтении глобальной переменной LastTradeTime");
return(-1);
}
// ждем Secs секунд
while(true)
{
// если эксперт остановлен, выйдем со значением 0
if (IsStopped())
{
Print("WaitBeforeTransaction(): эксперт остановлен. Выходим...");
return(0);
}
// если прошло меньше Secs секунд, то ждем
if ((LocalTime()-LastTradeTime)
// т.к. прошло больше Secs секунд, то попробуем изменить значение
// глобальной переменной LastTradeTime на текущее время
// используем функцию GlobalVariableSetOnCondition(), чтобы выявить
// ошибку, если уже за время ожидания другой эксперт успел совершить
// сделку и изменил значение глобальной переменной
if (GlobalVariableSetOnCondition("LastTradeTime", LocalTime(), LastTradeTime))
{
// за время ожидания глобальная переменная не изменилась, поэтому
// удалось установить ее новое значение
// пауза выдержена, обновим котировки и выйдем без ошибки
RefreshRates();
return(1);
}
else
{
// не удалось изменить значение глобальной переменной, т.к. другой
// эксперт успел совершить сделку раньше и установил новое
// значение переменной поэтому получим текущее значение глобальное
// переменной и продолжим ожидание
LastTradeTime = GlobalVariableGet("LastTradeTime");
// если произошла ошибка (равна нулю), то выходим с ошибкой
if (LastTradeTime==0)
{
Print("WaitBeforeTransaction(): ошибка ",GetLastError(),
" при чтении глобальной переменной LastTradeTime");
return(-1);
}
}
}
}
Определение режима тестирования на истории с помощью функции IsTesting()
Сам исходный код функции обладает достаточным количеством
комментариев, и я уверен, что логика реализации этой функции Вам станет
абсолютно понятна после изучения следующих функций:
- IsTesting()
- GlobalVariableCheck()
- GlobalVariableSet()
- GlobalVariableGet()
- LocalTime()
Начнем с рассмотрения функции IsTesting().
bool IsTesting()
Функция IsTesting() возвращает true, если эксперт работает в режиме тестирования на исторических данных, и false — если работа ведется на демо- или реальном счете.
Дело в том, что необязательно сразу заставлять эксперта торговать на
демо- или реальном счете. Гораздо эффективнее вначале оттестировать
эксперта на исторических данных, которые есть в клиентском терминале.
После этого с определенной точностью станет понятно, на что способен
этот эксперт.
Основное преимущество тестов на истории — быстрота. Вам не надо ждать
месяцы или годы, чтобы оттестировать своего эксперта на реальных
котировках. Вы просто «прогоняете» советника по истории. На это у Вас
уходит лишь несколько минут. Однако если Вы будете каждый раз ждать 10
секунд между торговыми операциями, то Вы потеряете это преимущество.
Именно поэтому в коде нашей функции присутствует строка:
if (IsTesting()) return(1);
Т.е. мы выходим из функции, если эксперт находится в режиме тестирования на историчексих данных.
- Читать Закрыть 42
Глобальные переменные.
GlobalVariableCheck(): проверка существования глобальной переменной
Для человека, имеющего уже опыт написания экспертов,
выражение «глобальные переменные» может внести небольшую путанницу. Дело
в том, что на самом деле существует два вида «глобальных переменных»:
- переменные, которые видны из любой функции ОДНОГО эксперта (т.е. являются глобальными в пределах этого эксперта); и
- переменные, которые являются общими для ВСЕХ экспертов.
О глобальных переменных первого типа — тех, которые являются общими
для всех функций ОДНОГО эксперта, — я рассказывал ранее, в выпуске
«Глобальные переменные».
Сейчас же речь пойдет именно о глобальных переменных второго типа — общих для ВСЕХ экспертов.
Глобальная переменная второго типа — это переменная, к которой можно
обратиться из любого эксперта. Если в течение четырех недель не было
сделано попытки прочесть значение глобальной переменной или записать в
нее новое значение, то глобальная переменная автоматически удаляется
клиентским терминалом.
Для работы с глобальными переменными используются следующие функции:
- GlobalVariableCheck()
- GlobalVariableDel()
- GlobalVariableGet()
- GlobalVariableName()
- GlobalVariableSet()
- GlobalVariableSetOnCondition()
- GlobalVariablesDeleteAll()
- GlobalVariablesTotal()
Вначале я хотел бы рассказать о функции GlobalVariableCheck().
bool GlobalVariableCheck(string name)
Эта функция возвращает true, если глобальная переменная с именем name существует, и false, если такой переменной нет.
В нашей функции WaitBeforeTransaction() мы храним время и дату
совершения последней торговой операции в глобальной переменной
«LastTradeTime».
Перед тем, как узнать время последней операции, прочитав значение этой переменной, мы проверяем, существует ли она вообще:
// если глобальная переменная LastTradeTime не существует,
// то создать ее
if (!GlobalVariableCheck("LastTradeTime"))
{
... создаем переменную ...
}
Проверять существование глобальной переменной и создавать ее в случае
отсутствия можно в двух местах: при инициализации эксперта — в функции
init() — или на каждом тике — в функции start().
Несмотря на то, что на первый взгляд напрашивается осуществлять эти действия в функции init(), я бы советовал Вам делать это все-таки в функции start().
Дело в том, что если пользователь при работающем эксперте удалит
глобальную переменную, а проверка на ее существование делается только
при инициализации эксперта, то дальнейшее поведение такого советника
сложно спрогнозировать. Скорее всего, такой советник просто перестанет
торговать и начнет выдавать ошибки при обращении к удаленной глобальной
переменной.
GlobalVariableSet() — установка нового значения глобальной переменной
Предположим, что ее не существует или же нам надо изменить ее значение. В этом случае надо использовать функцию GlobalVariableSet().
datetime GlobalVariableSet(string name, double value)
Функция GlobalVariableSet() устанавливает новое значение value глобальной переменной с именем name
и в случае успеха возвращает время последнего доступа к глобальной
переменной. Если произошла какая-то ошибка, то функция возвращает 0. Код
ошибки, как обычно, можно получить с помощью функции GetLastError().
Если глобальная переменная с именем name отсутствовала, то она создается и она принимает значение value.
Пример использования функции GlobalVariableSet() можно найти в написанной нами функции WaitBeforeTransaction():
// если глобальная переменная LastTradeTime не существует,
// то создать ее
if (!GlobalVariableCheck("LastTradeTime"))
{
// Если произошла какя-то ошибка при вызове функции
// GlobalVariableCheck(), выйдем с ошибкой
if (GetLastError()!=0)
{
Print("WaitBeforeTransaction(): ошибка ",GetLastError(),
" при проверке глобальной переменной LastTradeTime");
return(-1);
}
// глобальная переменная не существует, создадим ее
if (GlobalVariableSet("LastTradeTime", 1)==0)
{
// произошла ошибка при создании глобальной переменной
Print("WaitBeforeTransaction(): ошибка ",GetLastError(),
" при создании глобальной переменной LastTradeTime");
return(-1);
}
// глобальная переменная успешно создана
Print("WaitBeforeTransaction(): глобальная переменная ",
"LastTradeTime создана");
}
GlobalVariableSetOnCondition() — установка нового значения
глобальной переменной, если текущее ее значение равно заданному значению
К счастью, создатели языка MetaQuotes Language 4 являются
профессиональными программистами и заранее могут предугадать, что может
понадобиться трейдеру при написании экспертов. Я им очень благодарен,
что они включили в список функций языка MetaQuotes Language 4 функцию GlobalVariableSetOnCondition().
Давайте же рассмотрим, чем так полезна эта функция.
bool GlobalVariableSetOnCondition(string name, double value, double check_value)
Прежде всего эта функция проверяет, существует ли глобальная переменная с именем name. В документации по MetaQuotes Language 4 написано, что если такой глобальной переменной нет, то функция возвращает false и генерит ошибку ERR_GLOBAL_VARIABLE_NOT_FOUND (4058), которую можно получить с помощью функции GetLastError().
Однако при экспериментах с функцией GlobalVariableSetOnCondition()
мне не удалось добиться того, чтобы функция вернула ошибку
ERR_GLOBAL_VARIABLE_NOT_FOUND (4058). Почему-то даже в случае отсутствия
глобальной переменной функция возвращает false и код ошибки ERR_NO_ERROR (т.е. 0). Вот кусок кода, который я использовал для этой цели:
string name = "MyGlobalVariable";
double value = 1;
double check_value = 2;
if (GlobalVariableSetOnCondition(name, value, check_value))
{
Print("Глобальная переменная ", name, " существует.",
" Ее значение изменено с ", check_value, " на ", value);
}
else
{
int Err=0;
Err = GetLastError();
Print(Err);
switch (Err)
{
case 0: Print("Глобальная переменная ", name," изменена не была",
" т.к. ее значение не равно ", check_value); break;
case 4058: Print("Глобальной переменной ", name,
" не существует"); break;
default: Print("Неизвестная ошибка: ", Err);
}
}
Если это баг, то думаю, что в следующих версиях MetaTrader 4 его исправят.
Вернемся к описанию функции GlobalVariableSetOnCondition(). Если же глобальная переменная name существует, то функция не изменит ее текущего значения и вернет false, если текущее значение этой глобальной переменной не равно check_value.
Если потом запросить код последней ошибки, то функция
GetLastError()вернет ERR_NO_ERROR (т.е. 0), т.к. реально никакой ошибки
не было.
Если же текущее значение глобальной переменной name равно значению check_value, то функция GlobalVariableSetOnCondition() присвоит этой глобальной переменной новое значение: value.
- Читать Закрыть 43
Как дождаться освобождения торгового потока.
Пример использования функции GlobalVariableSetOnCondition()
В этом же выпуске я хотел бы рассмотреть пример ее практического использования.
//+---------------------------------------------------------------------+
//| int StartTrading() |
//| |
//| Функция занимает торговый поток |
//| Функция возвращает: |
//| 0 - если можно торговать |
//| 1 - если работа эксперта была остановлена |
//| 2 - если торговля экспертов запрещена на уровне настроек|
//| клиентского терминала |
//+---------------------------------------------------------------------+
#define GLOB_VAR_NAME "TradeIsAllowed"
int StartTrading()
{
// если советник работает в режиме тестирования, то просто выйдем
if (IsTesting()) return(0);
int LastError;
while (!IsStopped())
{
// Если торговля экспертов запрещена на уровне настроек
// клиентского терминала, то выйдем и вернем 2
if (!IsExpertEnabled()) return(2);
// проверим, существует ли глобальная переменная
if (GlobalVariableCheck(GLOB_VAR_NAME)) break;
// если произошла ошибка при проверке глобальной переменной,
// то сообщим об этом в логах и выждем 0.1 секунды
LastError = GetLastError();
if (LastError!=0)
{
Print("StartTrading(): ошибка ", LastError,
" при проверке наличия глобальной переменной ", GLOB_VAR_NAME);
Sleep(100);
continue;
}
// глобальная переменная не существует - создадим ее
if (GlobalVariableSet(GLOB_VAR_NAME, 0)>0) break;
// произошла ошибка при создании переменной - запишем в лог
LastError = GetLastError();
Print("StartTrading(): ошибка ", LastError,
" при создании глобальной переменной ", GLOB_VAR_NAME);
Sleep(100);
}
// глобальная переменная существует или же эксперт был остановлен
// В цикле проверяем, как изменилась ситуация
while (!IsStopped())
{
// Если торговля экспертов запрещена на уровне настроек
// клиентского терминала, то выйдем и вернем 2
if (!IsExpertEnabled()) return(2);
// Если удалось изменить значение глобальной переменной, то обновим данные
// о текущих курсах и вернем 0
if (GlobalVariableSetOnCondition(GLOB_VAR_NAME, 1, 0))
{
RefreshRates();
return(0);
}
// Если дошли до момента, то ситуация не изменилась
// Поэтому делаем паузу в 0.1 секунду
Sleep(100);
}
// Т.к. вышли из цикла, то работа эксперта была остановлена
// Вернем 1
return(1);
}
//+---------------------------------------------------------------------+
//| int StopTrading() |
//| |
//| Функция разрешает торговать следующему эксперту |
//| Функция возвращает: |
//| 0 - если другому эксперту можно торговать |
//| 1 - если работа эксперта была остановлена |
//| 2 - если торговля экспертов запрещена на уровне настроек|
//| клиентского терминала |
//+---------------------------------------------------------------------+
#define GLOB_VAR_NAME "TradeIsAllowed"
int StopTrading()
{
// если советник работает в режиме тестирования, то просто выйдем
if (IsTesting()) return(0);
int LastError;
while (!IsStopped())
{
// Если торговля экспертов запрещена на уровне настроек
// клиентского терминала, то выйдем и вернем 2
if (!IsExpertEnabled()) return(2);
// Если удалось изменить значение глобальной переменной, то обновим данные
// о текущих курсах и вернем 0
if (GlobalVariableSet(GLOB_VAR_NAME, 0)>0)
{
return(0);
}
// при сбросе значения глобальной переменной произошла какая-то ошибка
LastError = GetLastError();
Print("StopTrading(): ошибка ", LastError,
" при сбросе значения глобальной переменной ", GLOB_VAR_NAME);
// делаем паузу в 0.1 секунду
Sleep(100);
}
// Т.к. вышли из цикла, то работа эксперта была остановлена
// Вернем 1
return(1);
}
- Читать Закрыть 44
Критическая секция: разграничение доступа к ресурсу.
Расскажем о способе организации «критической секции» в советнике.
Дело в том, что если в момент выполнения этих функций будет
осуществлен доступ к массивам данных, хранимых в глобальных переменных,
из нескольких советников одновременно, то результат будет
непредсказуемым и в большинстве случаев данные будут испорчены.
Для того, чтобы избежать этого, мы создадим объект «критическая секция», который может находиться в двух состояниях:
- «зеленый свет» (-1); и
- «красный свет» (1).
В каждый конкретный момент только один советник может получить доступ
к данным. Пока советник получает данные, будет гореть «красный свет» и
другие советники будут ждать «зеленового света». Если горит «зеленый
свет», то дорога свободна, т.к. никто в данный момент к данным не
обращается.
Напишем две функции:
- Lock() — вызываем ее перед началом работы с ресурсом. Функция ждет «зеленового света» и меняет его на «красный».
- Unlock() — обязательно вызываем ее после окончания работы с ресурсом, чтобы снова зажечь «зеленый свет».
Вот исходный код этих функций:
//+------------------------------------------------------------------+
//| Lock() |
//| |
//| Возвращает: |
//| 0 - если "критическая секция" успешно |
//| заблокирована |
//| 1 - в случае ошибки |
//| 2 - эксперт остановлен |
//| 3 - по таймауту (слишком долго ждали) |
//+------------------------------------------------------------------+
int Lock(string GlobVarName, int timeout = 0)
{
string critical_section = GlobVarName+"Lock";
// проверим, существует ли переменная critical_section
if (!GlobalVariableCheck(critical_section))
{
if (GetLastError()!=0) return(1);
// переменная не существует, создадим ее
if (GlobalVariableSet(critical_section, -1.0)==0) return(1);
// переменная создана
}
int StartTime = GetTickCount();
// ждем "зеленового света"
while (true)
{
// проверить, не загорелся ли "зеленый свет"
if (GlobalVariableGet(critical_section)==-1.0)
{
// "зеленый свет" загорелся, зажигаем "красный свет"
if (GlobalVariableSetOnCondition(critical_section, 1.0, -1.0)) return(0);
// нас опередили, поэтому ждем "зеленового света"
}
// проверим, не остановлен ли эксперт
if (IsStopped()) return(2);
// таймаут не истек?
if (timeout!=0)
{
if ((GetTickCount()-StartTime)>timeout*1000) return(3);
}
// спим 0.1 секунды
Sleep(100);
}
}
//+------------------------------------------------------------------+
//| Unlock() |
//| |
//| Возвращает: |
//| 0 - если "критическая секция" успешно |
//| разблокирована |
//| 1 - в случае ошибки |
//| 2 - эксперт остановлен |
//| 3 - по таймауту (слишком долго ждали) |
//+------------------------------------------------------------------+
int Unlock(string GlobVarName, int timeout = 0)
{
string critical_section = GlobVarName+"Lock";
// проверим, существует ли переменная critical_section
if (!GlobalVariableCheck(critical_section))
{
if (GetLastError()!=0) return(1);
// переменная не существует, создадим ее
if (GlobalVariableSet(critical_section, -1.0)==0) return(1);
// переменная создана, поэтому выходим
return(-1.0);
}
int StartTime = GetTickCount();
// бесконечный цикл
while (true)
{
// пытаемся установить "зеленый свет"
if (GlobalVariableSetOnCondition(critical_section, -1.0, 1.0)) return(0);
// проверим, не остановлен ли эксперт
if (IsStopped()) return(2);
// таймаут не истек?
if (timeout!=0)
{
if ((GetTickCount()-StartTime)>timeout*1000) return(3);
}
// спим 0.1 секунды
Sleep(100);
}
}
- Читать Закрыть 45
функция OrderLots().
Очень часто необходимо указать количество лотов открытой позиции или
отложенного ордера. Самой простой вариант получить это значение — это
выделить позицию или ордер с помощью функции OrderSelect(), а потом
вызвать функцию OrderLots().
double OrderLots()
Функция возвращает количество лотов в выделенном ордере или позиции.
Как уже сказано, основное применение функции — передать количество лотов в качестве параметра в функции OrderClose() и OrderModify().
Забегая вперед, расскажем о формате функции OrderClose():
bool OrderClose(int ticket, double lots, double price, int slippage, color Color=CLR_NONE)
Где:
- ticket — тикер ордера или позиции;
- lots — количество лотов для закрытия позиции;
- price — цена закрытия;
- slippage — значение максимального проскальзывания в пунктах;
- color — цвет стрелки закрытия на графике. Если
параметр отсутствует или его значение равно CLR_NONE, то стрелка на
графике не отображается.
Как Вы видите, в качестве второго параметра нам нужно передать в
функцию количество лотов. Самый простой способ сделать это — получить
количество лотов с помощью функции OrderLots():
// закрыть позицию с тикером 77777 по текущей цене
// предположим, что позиция уже выделена с помощью OrderSelect
// и мы точно знаем, что это открытая позиция
if (OrderType() == OP_SELL)
OrderClose(OrderTicket(), OrderLots(), Ask, 3);
else
OrderClose(OrderTicket(), OrderLots(), Bid, 3);
В этом примере нам встретились две предопределенные переменные типа double:
- Bid — бид последней котировки по текущему инструменту (к которому прикреплен советник);
- Ask — аск последней котировки по текущему инструменту.
- Читать Закрыть 46
функция OrderExpiration().
|
|
» Статистика |
Онлайн всего: 1 Гостей: 1 Пользователей: 0
|
|