//+------------------------------------------------------------------+
//| OnTesterInit_Sample.mq5 |
//| Copyright 2018, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#property description "Пример советника с обработчиком OnTesterInit()"
#property description "в котором устанавливаются зачения и границы "
#property description "входных параметров при оптимизации"
input double lots=0. 1; // объем в лотах
input double kATR=3; // длина сигнальной свечи в ATR
input int ATRperiod=20; // период индикатора ATR
input int holdbars=8; // сколько баров удерживаем позицию
input int slippage=10; // допустимое проскальзывание
input bool revers=false; // переворачиваем сигнал?
input ulong EXPERT_MAGIC=0; // MagicNumber эксперта
//--- для хранения хендла индикатора ATR
int atr_handle;
//--- здесь будем хранить последние значения ATR и тела свечи
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//--- запоминаем время начала оптимизации
datetime optimization_start;
//--- для вывода на график длительности после окончании оптимизации
string report;
//+------------------------------------------------------------------+
//| TesterInit function |
//+------------------------------------------------------------------+
void OnTesterInit()
{
//--- установим значения входных параметров для оптимизации
ParameterSetRange("lots",false,0.1,0,0,0);
ParameterSetRange("kATR",true,3.0,1.0,0.3,7.0);
ParameterSetRange("ATRperiod",true,10,15,1,30);
ParameterSetRange("holdbars",true,5,3,1,15);
ParameterSetRange("slippage",false,10,0,0,0);
ParameterSetRange("revers",true,false,false,1,true);
ParameterSetRange("EXPERT_MAGIC",false,123456,0,0,0);
Print("Установлены начальные значения и границы параметров оптимизации");
//--- запомним начало оптимизации
optimization_start=TimeLocal();
report=StringFormat("%s: оптимизация запущена в %s",
__FUNCTION__,TimeToString(TimeLocal(),TIME_MINUTES|TIME_SECONDS));
//--- выведем собщения на график и в журнал терминала
Print(report);
Comment(report);
//---
}
//+------------------------------------------------------------------+
//| TesterDeinit function |
//+------------------------------------------------------------------+
void OnTesterDeinit()
{
//--- продолжительность оптимизации
string log_message=StringFormat("%s: оптимизация заняла %d секунды",
__FUNCTION__,TimeLocal()-optimization_start);
PrintFormat(log_message);
report=report+"\r\n"+log_message;
Comment(report);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- инициализируем глобальные переменные
last_atr=0;
last_body=0;
//--- установим правильный объем
double min_lot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
trade_lot=lots>min_lot? lots:min_lot;
//--- создадим хендл индикатора ATR
atr_handle=iATR(_Symbol,_Period,ATRperiod);
if(atr_handle==INVALID_HANDLE)
{
PrintFormat("%s: не удалось создать iATR, код ошибки %d",__FUNCTION__,GetLastError());
return(INIT_FAILED);
}
//--- успешная инициализация эксперта
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- торговый сигнал
static int signal=0; // +1 означает сигнал на покупку, -1 означает сигнал на продажу
//--- проверим и закроем старые позиции, открытые более holdbars баров назад
ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- проверим появление нового бара
if(isNewBar())
{
//--- проверим наличие сигнала
signal=CheckSignal();
}
//--- если открыта неттинговая позиция, то сигнал пропускаем - ждем, пока она закроется
if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
{
signal=0;
return; // выходим из обработчика события NewTick и не входим в рынок до появления нового бара
}
//--- для хеджингового счета каждая позиция живет и закрывается раздельно
if(signal!=0)
{
//--- сигнал на покупку
if(signal>0)
{
PrintFormat("%s: Есть сигнал на покупку! Revers=%s",__FUNCTION__,string(revers));
if(Buy(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
//--- сигнал на продажу
if(signal<0)
{
PrintFormat("%s: Есть сигнал на продажу! Revers=%s",__FUNCTION__,string(revers));
if(Sell(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
}
//--- конец функции OnTick
}
//+------------------------------------------------------------------+
//| Проверяет наличие торгового сигнала |
//+------------------------------------------------------------------+
int CheckSignal()
{
//--- 0 означает отсутствие сигнала
int res=0;
//--- получим значение ATR на предпоследнем завершенном баре (индекс бара равен 2)
double atr_value[1];
if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1)
{
last_atr=atr_value[0];
//--- получим данные последнего закрытого бара в массив типа MqlRates
MqlRates bar[1];
if(CopyRates(_Symbol,_Period,1,1,bar)!=-1)
{
//--- вычислим размер тела бара на последнем закрытом баре
last_body=bar[0].close-bar[0].open;
//--- если тело последнего бара (с индексом 1) превышает предыдущее значение ATR (на баре с индексом 2), то торговый сигнал получен
if(MathAbs(last_body)>kATR*last_atr)
res=last_body>0?1:-1; // для растущей свечи положительное значение
}
else
PrintFormat("%s: Не удалось получить последний бар! Ошибка",__FUNCTION__,GetLastError());
}
else
PrintFormat("%s: Не удалось получить значение индикатора ATR! Ошибка",__FUNCTION__,GetLastError());
//--- если включен реверсивный режим торговли
res=revers?-res:res; // если нужно, то развернем сигнал (вместо 1 вернем -1, а вместо -1 вернем +1)
//--- вернем значение торгового сигнала
return (res);
}
//+------------------------------------------------------------------+
//| Возвращает true при появлении нового бара |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
{
static datetime bartime=0; // храним время открытия текущего бара
//--- получим время открытия нулевого бара
datetime currbar_time=iTime(_Symbol,_Period,0);
//--- если время открытия изменилось, значит появился новый бар
if(bartime!=currbar_time)
{
bartime=currbar_time;
lastbar_timeopen=bartime;
//--- нужно ли выводить в лог информацию о времени открытия нового бара
if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER)))
{
//--- выведем сообщение о времени открытия нового бара
PrintFormat("%s: new bar on %s %s opened at %s",__FUNCTION__,_Symbol,
StringSubstr(EnumToString(_Period),7),
TimeToString(TimeCurrent(),TIME_SECONDS));
//--- получим данные о последнем тике
MqlTick last_tick;
if(!SymbolInfoTick(Symbol(),last_tick))
Print("SymbolInfoTick() failed, error = ",GetLastError());
//--- выведем время последнего тика с точностью до миллисекунд
PrintFormat("Last tick was at %s.%03d",
TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000);
}
//--- у нас есть новый бар
return (true);
}
//--- нового бара нет
return (false);
}
//+------------------------------------------------------------------+
//| Покупка по рынку с заданным объемом |
//+------------------------------------------------------------------+
bool Buy(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- покупаем по рыночной цене
return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| Продажа по рынку с заданным объемом |
//+------------------------------------------------------------------+
bool Sell(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- продаем по рыночной цене
return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| Закрытие позиций по времени удержания в барах |
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong magicnumber=0)
{
int total=PositionsTotal(); // количество открытых позиций
//--- перебор всех открытых позиций
for(int i=total-1; i>=0; i--)
{
//--- параметры позиции
ulong position_ticket=PositionGetTicket(i); // тикет позиции
string position_symbol=PositionGetString(POSITION_SYMBOL); // символ
ulong magic=PositionGetInteger(POSITION_MAGIC); // MagicNumber позиции
datetime position_open=(datetime)PositionGetInteger(POSITION_TIME); // время открытия позиции
int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1; // сколько баров назад была открыта позиция
//--- если позиция живет уже долго, а также MagicNumber и символ совпадают
if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol)
{
int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // количество знаков после запятой
double volume=PositionGetDouble(POSITION_VOLUME); // объем позиции
ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // тип позиции
string str_type=StringSubstr(EnumToString(type),14);
StringToLower(str_type); // понижаем регистр текста для правильного форматирования сообщения
PrintFormat("Закрываем позицию #%I64u %s %s %.2f",
position_ticket,position_symbol,str_type,volume);
//--- установка типа ордера и отправки торгового запроса
if(type==POSITION_TYPE_BUY)
MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket);
else
MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket);
}
}
}
//+------------------------------------------------------------------+
//| Подготовка и отправка торгового запроса |
//+------------------------------------------------------------------+
bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0)
{
//--- объявление и инициализация cтруктур
MqlTradeRequest request={};
MqlTradeResult result={};
double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
if(type==ORDER_TYPE_BUY)
price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
//--- параметры запроса
request.action =TRADE_ACTION_DEAL; // тип торговой операции
request.position =pos_ticket; // тикет позиции, если закрываем
request.symbol =Symbol(); // символ
request.volume =volume; // объем
request.type =type; // тип ордера
request.price =price; // цена совершения сделки
request.deviation=slip; // допустимое отклонение от цены
request.magic =magicnumber; // MagicNumber ордера
//--- отправка запроса
if(!OrderSend(request,result))
{
//--- выведем информацию о неудаче
PrintFormat("OrderSend %s %s %.2f at %.5f error %d",
request.symbol,EnumToString(type),volume,request.price,GetLastError());
return (false);
}
//--- сообщим об успешной операции
PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order);
return (true);
}
|