//+------------------------------------------------------------------+//| Expert tick function |//+------------------------------------------------------------------+voidOnTick()
{
//--- Зададим условия для открытия позиций BUY и SELL double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
price=NormalizeDouble(price,digits);
//-- Если открытой позиции нет и время для открытия позволяет,
//-- открывает BUY или SELL в зависимости от положения тика
if(IsMainPositionOpen() == false && IsTimeForOpen())
{
if(TickUP()==(price+point))
OpenBUY();
if(TickDOWN()==(price-point))
OpenSELL();
}
//-- Если наступило время закрытия позиции, закрываем все
if(IsTimeForClose())
CloseALL();
//+------------------------------------------------------------------+//| Шаблон трейлинг стопа предоставленный Василием Соколовым |//+------------------------------------------------------------------+//-- Выбираем позиции по текущему символу. Если позиции нет и выбирать нечего, то выходим!if(!PositionSelect(Symbol()))
return;
//-- Стоп-лосс длинной позиции переставляем в безубыток и тралим егоif(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
{
SetBreakevenForBuyPosition(); // установить безубыток для Buy позиции
TrailingStopLossForBuyPosition(); // перетащить Stop Loss для Buy позиции
}
//-- Стоп-лосс короткой позиции переставляем в безубыток и тралим егоelseif(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
SetBreakevenForSellPosition(); // установить безубыток для Sell позиции
TrailingStopLossForSellPosition(); // перетащить Stop Loss для Sell позиции
}
}
//+------------------------------------------------------------------+//| Возвращает истину, если позиция торгового эксперта уже открыта. |//| Возвращает ложь в противном случае. |//+------------------------------------------------------------------+ bool IsMainPositionOpen()
{
//-- Если позиций нет - то и у эксперта позиции нет, возвращаем ложь.if(PositionSelect(Symbol()) == false)
returnfalse;
//-- Позиция есть и ее мэджик совпадает с мэджиком эксперта - возвращаем истинуif(PositionGetInteger(POSITION_MAGIC) == Magic_Number)
returntrue;
//-- Позиция есть но ее мэджик не совподает с мэджиком эксперта -//-- это чья-то другая позиция, позвращаем ложьelsereturnfalse;
}
//+------------------------------------------------------------------+//| Возвращает истину, если текущее время попадает в диапазон |//| разрешенного времени для открытия позиции. В противном случае |//| возвращает ложь. |//+------------------------------------------------------------------+ bool IsTimeForOpen()
{
MqlDateTime time_current,time_open,time_open1;
TimeToStruct(TimeCurrent(),time_current);
TimeToStruct((D'1970.01.01 09:00:00'),time_open);
TimeToStruct((D'1970.01.01 09:01:00'),time_open1);
if(time_current.hour == time_open.hour &&
time_current.min >= time_open.min &&
time_current.min < time_open1.min
)
returntrue;
elsereturnfalse;
}
//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон |
//| времени для закрытия позиции. В противном случае возвращает ложь.| |
//+------------------------------------------------------------------+
bool IsTimeForClose()
{
MqlDateTime time_current,time_open,time_open1,time_close;
TimeToStruct(TimeCurrent(),time_current);
TimeToStruct((D'1970.01.01 23:50:00'), time_close);
if(time_current.hour==time_close.hour &&
time_current.min==time_close.min)
return true;
else
return false;
}
intOnInit()
{
//--- Определим тип счёта на который устанавливаем советник: демо или реальный счетENUM_ACCOUNT_TRADE_MODE account_type=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
//--- теперь превратим значение перечисления в понятный видstring trade_mode; //создадим переменную для торгового режимаACCOUNT_TRADE_MODE_CONTESTif(account_type==ACCOUNT_TRADE_MODE_REAL) //если торговый режим счёта - реальный
{
//--- выводим окно сообщений на торговом терминале и закрываем советникMessageBox("Работа на реальном счете запрещена, выходим!","Советник запущен на реальном счете");
return(INIT_FAILED); //возвращаем для функции OnInit ненулевое значение означающее "неудачная инициализация"
}
if(account_type==ACCOUNT_TRADE_MODE_DEMO) //если торговый режим счёта - демо
{
//--- выводим окно сообщений на торговом терминале и продолжаем работу советникаMessageBox("Работа на демо-счете разрешена!","Советник запущен на демо-счете");
trade_mode="Счёт REAL";
return(INIT_SUCCEEDED); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
}
//-- Заглушка на случай непредвиденных вариантов. Всегда должна быть даже если варианта явно два.
else
{
MessageBox("Неизвестный тип счета. Работа невозможна!");
return(INIT_FAILED); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
}
}
//+------------------------------------------------------------------+//| Возвращает истину, если позиция торгового эксперта уже открыта. |//| Возвращает ложь в противном случае. |//+------------------------------------------------------------------+ bool IsMainPositionOpen()
{
//-- Если позиций нет - то и у эксперта позиции нет, возвращаем ложь.if(PositionSelect(Symbol()) == false)
returnfalse;
//-- Позиция есть и ее мэджик совпадает с мэджиком эксперта - возвращаем истинуif(PositionGetInteger(POSITION_MAGIC) == Magic_Number)
returntrue;
//-- Позиция есть но ее мэджик не совподает с мэджиком эксперта -//-- это чья-то другая позиция, позвращаем ложьelsereturnfalse;
}
//+------------------------------------------------------------------+
/ | Expert initialization function |
//+------------------------------------------------------------------+/* Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции
return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то
это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше
можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.
"удачная инициализация".
*/intOnInit()
{
...
}
为什么要增加另一种类型的评论?只需用你的评论来取代块中的内容。
//+------------------------------------------------------------------------------------------------------+//| Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции |//| return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то |//| это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше |//| можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е. |//| "удачная инициализация". |//+------------------------------------------------------------------------------------------------------+intOnInit()
{
...
}
//+------------------------------------------------------------------+//| Возвращает истину, если текущее время попадает в диапазон |//| времени для закрытия позиции. В противном случае возвращает ложь.| |//+------------------------------------------------------------------+ bool IsTimeForClose()
{
MqlDateTime time_current,time_open,time_open1,time_close;
TimeToStruct(TimeCurrent(),time_current);
TimeToStruct((D'1970.01.01 23:50:00'), time_close);
if(time_current.hour==time_close.hour &&
time_current.min==time_close.min)
returntrue;
elsereturnfalse;
}
由于完美无缺,我将在代码上多加一些评论。
我用黄色标出了两个非琐碎的地方。
1) 注意代码在第一个if和下一个else中重复。唯一不同的是最后一行和结束动作(OpenBUY,OpenSell)。
2)进入其他区块的条件并不明显。由于有大量的?事实上,它们只取决于最后一行。
这是一个肯定的迹象,表明这里缺少一个功能。
我们需要写一个函数,如果开仓 时间与指定的时间一致,则返回真(我以后会写)。
是的,瓦西里,你是对的,我们真的应该写一个函数。
问候,弗拉基米尔。
顺便说一下,要注意你的程序的总大小。它已经相当大了。你喜欢它吗?顺便说一下,一个新手不能写这么大的代码量:变量会混在一起,括号会过大,编译错误会像雨后的蘑菇一样悄然出现。编译后,这样规模的程序开始出现故障,没有人能够理解哪里出了问题。而在你的代码中,由于某种原因,一切都能正常工作),而且在函数中很清楚发生了什么,如何发生的。一句话,它是一种美。
谢谢你,瓦西里。这里有很多你的工作,因为追踪止损模板是你提供的。我只需要用代码来填充这些功能。我目前正在研究追踪止损。有些事情已经做了,但为了向公众展示最终的EA版本,有些事情我还需要了解。
问候,弗拉基米尔。
我添加了一些功能。我最后的代码是这样的。
我还是不明白这些神奇的工作。在净空方面,它没有任何意义。在任何情况下,你都可以很容易地删除这个检查,因为它只在一个函数中完成。OnInit块也是想多了,还是写得不太正确。首先,你应该尽量写标识符,而不是数字。返回INIT_SUCCEEDED,而不是-1。第二,这里的开关是过度的。这段代码应该包含if或switch。首先你必须写一个,然后写另一个--只是油。
第三,我们需要监控所有的账户类型。我们有Demo,然后我们有Real。然后是竞赛。但是,即使没有第三个账户,也应该有一个存根,可以抓住所有其他的变体。
而锦上添花的是:评论。当我们写函数时,有很多空间,我们的手被吸引来把注释放在正确的代码块中。
当同样的代码被 "同时 "写在某个主函数的主体中时,它就会变得尽可能的短,而且没有注释。
所以多写一些函数,它们鼓励你写正确的注释,虽然它们使代码更加冗长,但也使它更加清晰。
第三点:你在这里写作。
为什么要增加另一种类型的评论?只需用你的评论来取代块中的内容。
记住,评论是给你的,而不是你给评论的。删除旧的,并添加你自己的。坚持格式--在函数头中,简要但清楚地在注释中说明该函数的作用,以及在什么情况下返回什么值。
顺便说一下,当职位关闭 时间条件被分离到一个单独的函数中时,很明显,它写得并不正确。
我从你的代码中提取了内部内容。很明显,只有在一分钟内才会平仓。В 23:50.这段代码会起作用,但如果在23:50时出了问题,那么在23:51时位置就会一直悬空。这就是为什么你至少要写出来。
而且,即使这种选择也不理想。一个更强大的解决方案是使用交易模式。然而,这是更高的水平。到目前为止,这种设计会做得很好。
你好,瓦西里!非常感谢你的及时建议和支持。您对函数的作用和程序代码构造原则的评论,对我学习MQL5编程语言真的很有帮助。
现在,我脑子里的信息已经结构化了,不仅是我写的代码,其他程序员写的代码也更容易理解。我希望这个主题对那些开始从头开始学习MQL5编程语言的人来说是一个很好的帮助。
问候,弗拉基米尔。
干得好,弗拉基米尔。好的编码员通常有好的算法,他们有好的目标设定者......。一切都始于正确的目标,和正确的目的。你可以一下子建好房子,然后找水。你可以先找到水,在建造房屋时考虑到水。目标/目的,以及目标设定是由机会....你直接到算法....但总的来说,它是非常好的!
顺便说一下,当职位关闭 时间条件被分离到一个单独的函数中时,很明显,它写得并不正确。
我从你的代码中提取了内部内容。很明显,只有在一分钟内才会平仓。В 23:50.这段代码会起作用,但如果在23:50时出了问题,那么在23:51时位置就会一直悬空。这就是为什么我们至少需要写出来。
而且,即使这种选择也不理想。一个更强大的解决方案是使用交易模式。然而,这是更高的水平。只要这个设计能够处理它。
瓦西里,你是一个伟大的老师!
没有任何入门读物或教科书能提供这样的解释。你建议的一切,我一定会在EA的最终版本中实施。
问候,弗拉基米尔。
我暂时偏离主题,给大家讲一个关于老师的人生故事。在我们的学院,当我们已经是专业的时候,我们有一个很好的老师。当时,我们正在研究逻辑的代数。许多学生在很长一段时间内无法理解1+1 如何能等于1?如果是1x1,显然就等于1。这就是你所拥有的!!!。这位老师用简单的例子给我们解释了什么是逻辑上的OR,什么是逻辑上的AND,这让我终生难忘。
老师说,想象一下,你需要在早上到达学院上课。你可以乘坐无轨电车或 有轨电车前往该学院。你已经来到了公交车站,但既没有无轨电车(条件为假 或与0 相同),也没有有轨电车(条件为假 或与0 相同)。自然,你将无法进入该研究所(条件 false 或与0 相同)。检查0+0=0。妙不可言!如果你到达公共汽车站时,有一辆无轨电车(条件为真 或与1 相同),或 一辆有轨电车(条件为真 或与1 相同),或 同时有一辆无轨电车和一辆有轨电车,那么你肯定会到达研究所,条件为真 或与1 相同将成立。检查:1+0=1,0+1=1 和1+1=1。一切都很合适!
他用无轨电车和有轨电车的同样例子,向我们解释了什么是逻辑和。
这就是教师天赋的力量!这就是教师天赋的力量。我将在我的余生中记住它!
真诚的,弗拉基米尔。
干得好。好的编码员通常有好的算法师,他们有目标设定者,他们有目标设定者......这一切都始于正确的目标,和正确的目的。你可以一下子建好房子,然后找水。你可以先找到水,在建造房屋时考虑到水。目标/目的,以及目标设定是由机会....你直接到算法....但总的来说,它是非常好的!
谢谢你,瓦莱里,感谢你对这个话题的参与和建设性的对话。
问候,弗拉基米尔。