Метод выявления ошибок в коде при помощи комментирования
Введение
В данной статье рассказывается о простом алгоритме поиска ошибок в коде MQL. Часто после написания программы возникают проблемы при компиляции, вызванные ошибками в коде. Это могут быть самые различные ошибки, но в любом случае возникает необходимость оперативного обнаружения участка кода, где допущена ошибка.
Нередко у людей уходит немало времени и масса нервов на поиски какой-нибудь лишней скобки. Однако есть способ быстрого обнаружения ошибок, который основан на использовании комментирования. Об этом методе я и расскажу в данной статье.
Концепция
Написать достаточно большой код без единой ошибки – весьма приятно. Но, к сожалению, так выходит не всегда. Есть даже шутка, что ни одна программа не была написана без единой ошибки. Я не рассматриваю здесь ошибки, которые приводят к неверному исполнению кода. Здесь пойдёт речь об ошибках, из-за которых становится невозможной компиляция.
Весьма распространённые ошибки – вставка лишней скобки в сложном условии, нехватка скобки, не выставление двоеточия, запятой (при объявлении переменных) и т.д. Часто при компиляции мы можем сразу увидеть, в какой строке допущена подобная ошибка. Но бывают и случаи, когда найти такую ошибку не так просто. Ни компилятор, ни зоркий глаз нам не могут помочь сходу найти ошибку. В таких случаях, как правило, начинающие (и не очень) программисты начинают "обходить" весь код, пытаясь визуально определить ошибку. И снова, и снова, пока нервы не иссякнут, и не будет сказано "проще заново написать!".
Однако MQL, как и другие языки программирования, предлагает потрясающий инструмент – комментирование. Используя его можно "убирать", "отключать" какие-то участки кода. Обычно комментирование используют именно для вставки каких-то комментариев, или же отключения неиспользуемых участков кода. Комментирование можно также успешно применять и при поиске ошибок.
Алгоритм поиска ошибок
Поиск ошибок обычно сводится к определению участка кода, где допущена ошибка, а затем, в этом участке, визуально находится ошибка. Думаю, вряд ли кто-то будет сомневаться в том, что исследовать "на глаз" 5-10 строчек кода проще и быстрей, чем 100-500.
При использовании комментирования задача предельно проста. Сначала нужно закомментировать различные участки кода (иногда чуть ли не весь код), тем самым "отключив" его. Затем, по очереди комментирование снимается с этих участков кода. После очередного снятия комментирования совершается попытка компиляции. Если компиляция прошла успешно – ошибка не в этом участке кода. Затем открывается следующий участок кода и т.д. Когда находится проблемный участок кода, визуально ищется ошибка, затем устраняется. Опять происходит попытка компиляции. Если всё прошло успешно, - ошибка устранена.
В случае возникновения новых ошибок, процедура повторяется до их устранения. Данный подход очень полезен при написании достаточно больших программ, но и нередко оправдывает себя и при написании относительно небольших кодов.
Весьма важно правильно определять участки кода, которые необходимо комментировать. Если это условие (или иная логическая конструкция) – то оно должно комментироваться полно. Если комментируется участок кода, где объявляются переменные, важно, чтобы не был открыт участок, где происходит обращение к этим переменным. Иначе говоря, комментирование должно применяться по логике программирования. Несоблюдения такого подхода приводит к возникновению новых, вводящих в заблуждение, ошибок при компиляции.
Пример
Приведу пример практического поиска ошибки в коде. Допустим, у нас есть некоторый код:
#property copyright "" #property link "" extern int Level1=6; extern int Level2=2; extern double Lots=0.1; extern int TP=7; extern int SL=5000; extern int Profit_stop=10; int start() { //+-----------------------------------------------------------------------------------------------------------+ //| поиск открытых ордеров по паре | int pos_sell=0; for (int i_op_sell=OrdersTotal()-1; i_op_sell>=0; i_op_sell--) { if (!OrderSelect(i_op_sell,SELECT_BY_POS,MODE_TRADES)) break; if (Symbol()==OrderSymbol()&&(OrderType()==OP_SELLSTOP||OrderType()==OP_SELL)&&(OrderComment()=="sar_ao")) { pos_sell=1; break; } } int pos_buy=0; for (int i_op_buy=OrdersTotal()-1; i_op_buy>=0; i_op_buy--) { if (!OrderSelect(i_op_buy,SELECT_BY_POS,MODE_TRADES)) break; if (Symbol()==OrderSymbol()&&(OrderType()==OP_BUYSTOP||OrderType()==OP_BUY)&&(OrderComment()=="sar_ao")) { pos_buy=1; break; } } //| поиск открытых ордеров по паре | //+-----------------------------------------------------------------------------------------------------------+ //+-----------------------------------------------------------------------------------------------------------+ //| стоп в безубыток | double stop_open; for (int ia=OrdersTotal()-1; ia>=0; ia--) { if (!OrderSelect(ia,SELECT_BY_POS,MODE_TRADES)) break; if ((OrderType()==OP_BUY)&&(Symbol()==OrderSymbol())&&(OrderComment()=="sar_ao")) { stop_open=OrderOpenPrice(); if (NormalizeDouble(Bid,Digits)-stop_open<=Profit_stop*Point) continue; OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()+1*Point,OrderTakeProfit(),OrderExpiration(),CLR_NONE); } if ((OrderType()==OP_SELL)&&(Symbol()==OrderSymbol())&&(OrderComment()=="sar_ao")) { stop_open=OrderOpenPrice(); if (stop_open-NormalizeDouble(Ask,Digits)<=Profit_stop*Point) continue; OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()-1*Point,OrderTakeProfit(),OrderExpiration(),CLR_NONE); } } //| стоп в безубыток | //+-----------------------------------------------------------------------------------------------------------+ int i; bool trend_UP=true,trend_DOWN=true; //+------------------------------------------------------------------------------- if(!pos_buy) { for(i=Level1; i>=0; i--) { if(Open[i]<iSAR(NULL,0,0.02,0.1,i)) { trend_UP=false; break; } } for(i=Level2*2; i>=0; i--) { if(i>Level2) { if(iAO(NULL, 0, i+1)<=iAO(NULL, 0, i)) { trend_UP=false; break; } } if(i<Level2) { if(iAO(NULL, 0, i+1)>=iAO(NULL, 0, i)) { trend_UP=false; break; } } } } else { trend_UP=false; } //*************************************************************************** if(!pos_sell) { for(i=Level1; i>=0; i--) { { if(Open[i]>iSAR(NULL,0,0.02,0.1,i)) { trend_DOWN=false; break; } } for(i=Level2*2; i>=0; i--) { if(i>Level2) { if(iAO(NULL, 0, i+1)>=iAO(NULL, 0, i)) { trend_DOWN=false; break; } } if(i<Level2) { if(iAO(NULL, 0, i+1)<=iAO(NULL, 0, i)) { trend_DOWN=false; break; } } } } else { trend_DOWN=false; } if(Open[0]>iSAR(NULL,0,0.02,0.2,0)) { ObjectDelete("sell"); } if(Open[0]<iSAR(NULL,0,0.02,0.2,0)) { ObjectDelete("buy"); } double MA_1; MA_1=iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_SIGNAL,0); if(trend_UP && MA_1<50 && Open[1]<Close[1] && !pos_buy && ObjectFind("buy") != 0) { OrderSend(Symbol(),OP_BUY, Lots,Ask,2,Ask-SL*Point,Ask+TP*Point,"sar_ao",0,0,Blue); ObjectCreate("buy", OBJ_ARROW, 0, Time[0], Bid); ObjectSet("buy", OBJPROP_STYLE, STYLE_DOT); ObjectSet("buy", OBJPROP_ARROWCODE, SYMBOL_ARROWUP); ObjectSet("buy", OBJPROP_COLOR, LightSeaGreen); } if(trend_DOWN && MA_1>50 && Open[1]>Close[1] && !pos_sell && ObjectFind("sell") != 0) { OrderSend(Symbol(),OP_SELL, Lots,Bid,2,Bid+SL*Point,Bid-TP*Point,"sar_ao",0,0,Red); ObjectCreate("sell", OBJ_ARROW, 0, Time[0], Bid); ObjectSet("sell", OBJPROP_STYLE, STYLE_DOT); ObjectSet("sell", OBJPROP_ARROWCODE, SYMBOL_ARROWDOWN); ObjectSet("sell", OBJPROP_COLOR, Red); } //+------------------------------------------------------------------------------- //---- return(0); } //+------------------------------------------------------------------+
При попытке его компиляции мы видим сообщение об ошибке:
Оперативно определить участок кода, где допущена ошибка, не представляется возможным. Прибегаем к комментированию. Комментируем все логические конструкции:
#property copyright "" #property link "" extern int Level1=6; extern int Level2=2; extern double Lots=0.1; extern int TP=7; extern int SL=5000; extern int Profit_stop=10; int start() { /* //+-----------------------------------------------------------------------------------------------------------+ //| поиск открытых ордеров по паре | int pos_sell=0; for (int i_op_sell=OrdersTotal()-1; i_op_sell>=0; i_op_sell--) { if (!OrderSelect(i_op_sell,SELECT_BY_POS,MODE_TRADES)) break; if (Symbol()==OrderSymbol()&&(OrderType()==OP_SELLSTOP||OrderType()==OP_SELL)&&(OrderComment()=="sar_ao")) { pos_sell=1; break; } } int pos_buy=0; for (int i_op_buy=OrdersTotal()-1; i_op_buy>=0; i_op_buy--) { if (!OrderSelect(i_op_buy,SELECT_BY_POS,MODE_TRADES)) break; if (Symbol()==OrderSymbol()&&(OrderType()==OP_BUYSTOP||OrderType()==OP_BUY)&&(OrderComment()=="sar_ao")) { pos_buy=1; break; } } //| поиск открытых ордеров по паре | //+-----------------------------------------------------------------------------------------------------------+ */ /* //+-----------------------------------------------------------------------------------------------------------+ //| стоп в безубыток | double stop_open; for (int ia=OrdersTotal()-1; ia>=0; ia--) { if (!OrderSelect(ia,SELECT_BY_POS,MODE_TRADES)) break; if ((OrderType()==OP_BUY)&&(Symbol()==OrderSymbol())&&(OrderComment()=="sar_ao")) { stop_open=OrderOpenPrice(); if (NormalizeDouble(Bid,Digits)-stop_open<=Profit_stop*Point) continue; OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()+1*Point,OrderTakeProfit(),OrderExpiration(),CLR_NONE); } if ((OrderType()==OP_SELL)&&(Symbol()==OrderSymbol())&&(OrderComment()=="sar_ao")) { stop_open=OrderOpenPrice(); if (stop_open-NormalizeDouble(Ask,Digits)<=Profit_stop*Point) continue; OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()-1*Point,OrderTakeProfit(),OrderExpiration(),CLR_NONE); } } //| стоп в безубыток | //+-----------------------------------------------------------------------------------------------------------+ */ /* int i; bool trend_UP=true,trend_DOWN=true; //+------------------------------------------------------------------------------- if(!pos_buy) { for(i=Level1; i>=0; i--) { if(Open[i]<iSAR(NULL,0,0.02,0.1,i)) { trend_UP=false; break; } } for(i=Level2*2; i>=0; i--) { if(i>Level2) { if(iAO(NULL, 0, i+1)<=iAO(NULL, 0, i)) { trend_UP=false; break; } } if(i<Level2) { if(iAO(NULL, 0, i+1)>=iAO(NULL, 0, i)) { trend_UP=false; break; } } } } else { trend_UP=false; } */ //*************************************************************************** /* if(!pos_sell) { for(i=Level1; i>=0; i--) { { if(Open[i]>iSAR(NULL,0,0.02,0.1,i)) { trend_DOWN=false; break; } } for(i=Level2*2; i>=0; i--) { if(i>Level2) { if(iAO(NULL, 0, i+1)>=iAO(NULL, 0, i)) { trend_DOWN=false; break; } } if(i<Level2) { if(iAO(NULL, 0, i+1)<=iAO(NULL, 0, i)) { trend_DOWN=false; break; } } } } else { trend_DOWN=false; } */ /* if(Open[0]>iSAR(NULL,0,0.02,0.2,0)) { ObjectDelete("sell"); } if(Open[0]<iSAR(NULL,0,0.02,0.2,0)) { ObjectDelete("buy"); } */ double MA_1; MA_1=iStochastic(NULL,0,5,3,3,MODE_SMA,0,MODE_SIGNAL,0); /* if(trend_UP && MA_1<50 && Open[1]<Close[1] && !pos_buy && ObjectFind("buy") != 0) { OrderSend(Symbol(),OP_BUY, Lots,Ask,2,Ask-SL*Point,Ask+TP*Point,"sar_ao",0,0,Blue); ObjectCreate("buy", OBJ_ARROW, 0, Time[0], Bid); ObjectSet("buy", OBJPROP_STYLE, STYLE_DOT); ObjectSet("buy", OBJPROP_ARROWCODE, SYMBOL_ARROWUP); ObjectSet("buy", OBJPROP_COLOR, LightSeaGreen); } */ /* if(trend_DOWN && MA_1>50 && Open[1]>Close[1] && !pos_sell && ObjectFind("sell") != 0) { OrderSend(Symbol(),OP_SELL, Lots,Bid,2,Bid+SL*Point,Bid-TP*Point,"sar_ao",0,0,Red); ObjectCreate("sell", OBJ_ARROW, 0, Time[0], Bid); ObjectSet("sell", OBJPROP_STYLE, STYLE_DOT); ObjectSet("sell", OBJPROP_ARROWCODE, SYMBOL_ARROWDOWN); ObjectSet("sell", OBJPROP_COLOR, Red); } */ //+------------------------------------------------------------------------------- //---- return(0); } //+------------------------------------------------------------------+
Легко можно убедиться, что такой код компилируется без проблем. Значит, участок кода, где допущена ошибка "скрыт". По очереди открываем участки кода /* ... */, пытаемся откомпилировать.
Компиляция будет происходить благополучно, пока мы не дойдём до участка кода:
//*************************************************************************** if(!pos_sell) { for(i=Level1; i>=0; i--) { { if(Open[i]>iSAR(NULL,0,0.02,0.1,i)) { trend_DOWN=false; break; } } for(i=Level2*2; i>=0; i--) { if(i>Level2) { if(iAO(NULL, 0, i+1)>=iAO(NULL, 0, i)) { trend_DOWN=false; break; } } if(i<Level2) { if(iAO(NULL, 0, i+1)<=iAO(NULL, 0, i)) { trend_DOWN=false; break; } } } } else { trend_DOWN=false; }
Следовательно, ошибка именно в этой логической конструкции. При детальном "осмотре" данного участка кода, можно увидеть, что поставлена лишняя фигурная скобка в данной конструкции:
for(i=Level1; i>=0; i--) { { if(Open[i]>iSAR(NULL,0,0.02,0.1,i)) { trend_DOWN=false; break; } }
Если убрать её, код благополучно откомпилируется.
Убрав оставшиеся комментарии, мы убедимся в том, что других ошибок в коде нет. Значит, цель достигнута - ошибка в коде была найдена достаточно оперативно.
Заключение
На практическом примере было показано, как именно используется данный алгоритм поиска ошибок. В данном примере используется весьма немаленький код (194 строки), и на его "обход" могло бы уйти достаточно много времени. Именно возможность комментирования экономит достаточно много времени у многих программистов, которые сталкиваются с задачей поиска ошибок.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
не очень понятно, для кого эта статья
человек нашёл для себя решение, о котором все давно знают и решил поделиться... только с кем, сам с собой...
ужасный, не красивый код.
не очень понятно, для кого эта статья
человек нашёл для себя решение, о котором все давно знают и решил поделиться... только с кем, сам с собой...
ужасный, не красивый код, от этого и ошибки.
В одном моём эксперте ~21 тыс. строк кода
....
И второе. после закрывающей } полезно также в комментарии ставить первоначальную конструкцию:
while (x < 5)
{
// insert code here
// ...
// many code pages...
} // while (x < 5)
Наглядно видно, к чему относится }.
Или просто нумеровать:
for ()...
{//1
if ()...
{//2
OdrerSend()...
}//2
}//1