Требуется гайд для новичка.

 
Всем привет.
Такой вопрос:
Есть где нибудь +- целостный гайд для MQL5(что бы не собирать по крупицам) - пусть и платный на тему того как написать собственный индикатор с нуля(для чайников) ?
Все что найди удается это варианты где уже пишут какие то скользящие средние и пр вещи.
Мне же увидеть необходимо(что бы разобратся как все это работает) пример кода как допустим нарисовать обычную линию сапорта по последним 3-м японским свечам.
То есть необходимо понять следующее:
Как вообще нарисовать точку хотя бы именно там на графике где я хочу(условно на 20 пикселей ниже(или выше или правее или левее) свечи)
далее как провести прямую линию от одной точке к другой.
(в идеале что бы еще понять можно было как замерить угол нарисованной уже потом линии сапорта - но на такую роскошь я даже не расчитываю честно говоря)
Есть у кого то ссылка на подобный код или мануал или что то в таком духе?
Moving Average of Oscillator (OsMA)
Moving Average of Oscillator (OsMA)
  • www.mql5.com
On Balance Volume (OBV) Индикатор Балансового Объема (On Balance Volume, OBV) связывает объем и изменение цены, сопровождавшее данный объем. Momentum Индикатор движущей силы рынка (Momentum) измеряет величину изменения цены финансового инструмента за определенный...
 
Я читал эти гайды - они не дают ответов на мои вопросы + примеры избыточны, больше запутывают и уводят от сути, чем помагают разобраться как происходит позиционирование пикселей при отрисовке индикаторов.
(и какие команды за это отвечают. Рисовать SMA - мне это точно ни к чему на данный момент. Мне не нужно рисовать готовые шаблонные SMA - мне необходимо понять как отрисовываются произвольные не шаблонные вещи - на подобие таких как я описал в изначальном вопросе в данном посте.)
 

Линию Вы можете нарисовать создав объект OBJ_TREND. Ниже функция, которая создаёт и перерисовывает линию при изменении координат или цвета. Координаты каждой из двух крайних точек - это дата свечи и цена. Здесь не используются пикселы, потому что линия привязывается к графику, а график может растягиваться как в высоту так и в ширину. Поэтому отступ, допустим от тени свечи, Вам придётся делать в единицах цены - пунктах. Например: 1.23456+20*Point=1.23476.

//перерисовывает линию по новым координатам, если её нет, то создаёт
void RedrawLine(long eChartId, int eWindow, string eName, datetime eTime1, double ePrice1, datetime eTime2, double ePrice2, color eColor, int eWidth, int eDigits)
   {
   if(ObjectFind(eChartId,eName)==-1)
      {
      if(!ObjectCreate(eChartId,eName,OBJ_TREND,eWindow,0,0)) return;
      ObjectSetInteger(eChartId,eName,OBJPROP_STYLE,STYLE_SOLID);
      ObjectSetInteger(eChartId,eName,OBJPROP_WIDTH,eWidth);
      ObjectSetInteger(eChartId,eName,OBJPROP_BACK,true);
      ObjectSetInteger(eChartId,eName,OBJPROP_SELECTABLE,false);
      ObjectSetInteger(eChartId,eName,OBJPROP_SELECTED,false);
      ObjectSetInteger(eChartId,eName,OBJPROP_RAY_RIGHT,false);
      ObjectSetInteger(eChartId,eName,OBJPROP_HIDDEN,true);
      }
   if(ObjectFind(eChartId,eName)==-1) return;   
   if(ObjectGetInteger(eChartId,eName,OBJPROP_TIME)!=eTime1) ObjectSetInteger(eChartId,eName,OBJPROP_TIME,eTime1);
   if(NormalizeDouble(ObjectGetDouble(eChartId,eName,OBJPROP_PRICE)-ePrice1,eDigits)!=0) ObjectSetDouble(eChartId,eName,OBJPROP_PRICE,ePrice1);
   if(ObjectGetInteger(eChartId,eName,OBJPROP_TIME,1)!=eTime2) ObjectSetInteger(eChartId,eName,OBJPROP_TIME,1,eTime2);
   if(NormalizeDouble(ObjectGetDouble(eChartId,eName,OBJPROP_PRICE,1)-ePrice2,eDigits)!=0) ObjectSetDouble(eChartId,eName,OBJPROP_PRICE,1,ePrice2);
   if(ObjectGetInteger(eChartId,eName,OBJPROP_COLOR)!=eColor) ObjectSetInteger(eChartId,eName,OBJPROP_COLOR,eColor);
   }

Можно конечно рассчитать сколько пунктов в одном пикселе, но необходимо будет следить в OnChartEvent за изменением размера графика. Когда пользователь изменит размер (сменит таймфрейм, изменит масштаб, растянет по вертикали), в этот момент соотношение пунктов к пикселю изменится. 

//подсчитывает количество пунктов цены в одном пикселе
double GetPointsInPixel(long eChartId, int eWindow)
   {
   double eMin=EMPTY_VALUE;
   ChartGetDouble(eChartId,CHART_PRICE_MIN,eWindow,eMin);
   if(eMin==EMPTY_VALUE) return(0);
   double eMax=EMPTY_VALUE;
   ChartGetDouble(eChartId,CHART_PRICE_MAX,eWindow,eMax);
   if(eMax==EMPTY_VALUE) return(0);
   long ePixels=-1;
   ChartGetInteger(eChartId,CHART_HEIGHT_IN_PIXELS,eWindow,ePixels);
   if(ePixels<=0) return(0);
   return(double(eMax-eMin)/ePixels);
   }

Тогда к тени свечи можно прибавить ровно 10 пикселей при любом масштабе: 1.23456+GetPointsInPixel(ChartId(),ChartWindowFind())*10. Но следите за изменением графика и перерисовывайте линию при его изменении!

Если линий много, получите тормоза при каждом изменении графика. Первый способ самый простой и достаточный.


 
Aleksei Stepanenko:

А что бы это все корретно скомпилировалось и заработало(имею ввиду первую часть вашего кода) - как необходимо интегрировать данную функцию?
Ложить ее в onCalculate или в OInit?(или в двух местах инициализировать все это дело?)
(хотелось бы увидеть какой то работающий пример который уже потом можно было бы до атома самому разобрать и понять принцип действия)

 

Индикатор показывает направление движения:

#property strict
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

input int Distance=500;    //Минимальное расстояние, пункты

//каждый элемент массива будет содержать три поля: дата, цена и положение экстремума (верх, низ) 
struct Extrem
   {
   datetime time;          //дата окончания тренда
   double price;           //цена окончания тренда
   int direction;          //направление тренда +1/-1
   } extremes[];
//последний элемент массива
int finish;

//текущий символ и таймфрейм
string symbol;
ENUM_TIMEFRAMES frame;

long chart_id;
int window;

//дата окончания обработанного периода
datetime calculation_time=0;
string name;

double point;
int digits;

int OnInit()
   {
   symbol=Symbol();
   frame=(ENUM_TIMEFRAMES)Period();
   chart_id=ChartID();
   window=ChartWindowFind();
   point=SymbolInfoDouble(symbol,SYMBOL_POINT);
   digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
   return(INIT_SUCCEEDED);
   }

void OnDeinit(const int eReason)
   {
   //удаляем все нарисованные объекты индикатора
   ObjectsDeleteAll(chart_id,"Mike");
   }

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
   {
   //время окончания цикла
   datetime finish_time=iTime(symbol,frame,0);
   //проходим циклом по всем свечам истории
   do
      {
      //записываем экстремумы в массив
      WriteExtremum(extremes,(double)Distance*point,symbol,frame,calculation_time);
      
      //последний элемент массива
      finish=ArraySize(extremes)-1;
      //рисуем зиг-заг, если массив заполнен
      if(finish>0)
         {
         //тренд вниз
         if(extremes[finish].direction==-1)
            {
            //сейчас происходит обновление минимума
            if(extremes[finish].time==calculation_time)
               {
               //имя состоит из названия индикатора, направления движения и времени начала движения
               name="Mike_Dn_"+TimeToString(extremes[finish-1].time);
               RedrawLine(chart_id,window,name,extremes[finish-1].time,extremes[finish-1].price,extremes[finish].time,extremes[finish].price,clrRed,1,digits);
               }
            }
         //тренд вверх
         else
            {
            //сейчас происходит обновление максимума
            if(extremes[finish].time==calculation_time)
               {
               //имя состоит из названия индикатора, направления движения и времени начала движения
               name="Mike_Up_"+TimeToString(extremes[finish-1].time);
               RedrawLine(chart_id,window,name,extremes[finish-1].time,extremes[finish-1].price,extremes[finish].time,extremes[finish].price,clrBlue,1,digits);
               }
            }      
         }
               
      //переводим время на один бар вперёд в будущее
      if(calculation_time<finish_time)
         {
         calculation_time=iTime(symbol,frame,(iBarShift(symbol,frame,calculation_time)-1));
         }
      else
         {
         break;
         }
      }
   while(calculation_time<=finish_time);
   return(rates_total);
   }

//здесь записываем экстремумы в массив
void WriteExtremum(Extrem &eExtremes[], double eDistance, string eSymbol, ENUM_TIMEFRAMES eTimeFrame, datetime eTime)
   {
   int eFinish=ArraySize(eExtremes)-1;
   int eShift=iBarShift(eSymbol,eTimeFrame,eTime);
   double eHigh=iHigh(eSymbol,eTimeFrame,eShift);
   double eLow=iLow(eSymbol,eTimeFrame,eShift);
   //если массив пустой
   if(eFinish<0)
      {
      ArrayResize(eExtremes,++eFinish+1);
      eExtremes[eFinish].time=eTime;
      //пока мы не знаем какой из экстремумов будет в первом элементе, поэтому берём цену по середине
      //можно сделать более грамотнее, но лень. Поэтому первый экстремум у нас будет бракованный 
      //и мы его потом затрём 
      eExtremes[eFinish].price=(eHigh+eLow)/2;
      eExtremes[eFinish].direction=0;
      }
   //если в массиве есть элементы
   else
      {
      //текущий элемент - максимум
      if(eExtremes[eFinish].direction==1)
         {
         //произошло обновление текущего экстремума
         if(eHigh-eExtremes[eFinish].price>0)
            {
            eExtremes[eFinish].time=eTime;
            eExtremes[eFinish].price=eHigh;
            }    
         else
            {     
            //произошло превышение расстояния между противоположными экстремумами
            if(eExtremes[eFinish].price-eLow>eDistance && eTime-eExtremes[eFinish].time>0)
               {
               ArrayResize(eExtremes,++eFinish+1,10000);
               eExtremes[eFinish].time=eTime;
               eExtremes[eFinish].price=eLow;
               eExtremes[eFinish].direction=-1;
               }
            }
         }
      //текущий элемент - минимум
      if(eExtremes[eFinish].direction==-1)
         {
         //произошло обновление текущего экстремума
         if(eExtremes[eFinish].price-eLow>0)
            {
            eExtremes[eFinish].time=eTime;
            eExtremes[eFinish].price=eLow;
            }    
         else
            {     
            //произошло превышение расстояния между противоположными экстремумами
            if(eHigh-eExtremes[eFinish].price>eDistance && eTime-eExtremes[eFinish].time>0)
               {
               ArrayResize(eExtremes,++eFinish+1,10000);
               eExtremes[eFinish].time=eTime;
               eExtremes[eFinish].price=eHigh;
               eExtremes[eFinish].direction=1;
               }
            }
         }
      //эта ситуация, когда первый элемент не закрылся, и не понятно максимум это будет или минимум
      //если произошло превышение в любую сторону, тогда затираем значения первого элемента
      if(eExtremes[eFinish].direction==0)
         {       
         //произошло превышение расстояния между противоположными экстремумами
         if(eHigh-eExtremes[eFinish].price>eDistance)
            {
            eExtremes[eFinish].time=eTime;
            eExtremes[eFinish].price=eHigh;
            eExtremes[eFinish].direction=1;
            }            
         if(eExtremes[eFinish].price-eLow>eDistance)
            {
            eExtremes[eFinish].time=eTime;
            eExtremes[eFinish].price=eLow;
            eExtremes[eFinish].direction=-1;
            }
         }
      }   
   }

//перерисовывает линию по новым координатам, если её нет, то создаёт
void RedrawLine(long eChartId, int eWindow, string eName, datetime eTime1, double ePrice1, datetime eTime2, double ePrice2, color eColor, int eWidth, int eDigits)
   {
   if(ObjectFind(eChartId,eName)==-1)
      {
      if(!ObjectCreate(eChartId,eName,OBJ_TREND,eWindow,0,0)) return;
      ObjectSetInteger(eChartId,eName,OBJPROP_STYLE,STYLE_SOLID);
      ObjectSetInteger(eChartId,eName,OBJPROP_WIDTH,eWidth);
      ObjectSetInteger(eChartId,eName,OBJPROP_BACK,true);
      ObjectSetInteger(eChartId,eName,OBJPROP_SELECTABLE,false);
      ObjectSetInteger(eChartId,eName,OBJPROP_SELECTED,false);
      ObjectSetInteger(eChartId,eName,OBJPROP_RAY_RIGHT,false);
      ObjectSetInteger(eChartId,eName,OBJPROP_HIDDEN,true);
      }
   if(ObjectFind(eChartId,eName)==-1) return;   
   if(ObjectGetInteger(eChartId,eName,OBJPROP_TIME)!=eTime1) ObjectSetInteger(eChartId,eName,OBJPROP_TIME,eTime1);
   if(NormalizeDouble(ObjectGetDouble(eChartId,eName,OBJPROP_PRICE)-ePrice1,eDigits)!=0) ObjectSetDouble(eChartId,eName,OBJPROP_PRICE,ePrice1);
   if(ObjectGetInteger(eChartId,eName,OBJPROP_TIME,1)!=eTime2) ObjectSetInteger(eChartId,eName,OBJPROP_TIME,1,eTime2);
   if(NormalizeDouble(ObjectGetDouble(eChartId,eName,OBJPROP_PRICE,1)-ePrice2,eDigits)!=0) ObjectSetDouble(eChartId,eName,OBJPROP_PRICE,1,ePrice2);
   if(ObjectGetInteger(eChartId,eName,OBJPROP_COLOR)!=eColor) ObjectSetInteger(eChartId,eName,OBJPROP_COLOR,eColor);
   }


 
Aleksei Stepanenko:

Индикатор показывает направление движения:


***
почему то 3 ошибки в коде найдено.
В чем тут проблема может быть?
 
Исправил код выше. Писал на МТ4, а МТ5 уже не знает зарезервированных слов Point и Digits 
Файлы:
Mike.mq5  17 kb
 
Aleksei Stepanenko:
Исправил код выше. Писал на МТ4, а МТ5 уже не знает зарезервированных слов Point и Digits 
***
Вроде навесил индикатор правильно - но никакой реакции нет(не работает.)
 

Начинает работать после прихода тика по текущей паре. Не знаю почему так, работаю в МТ4 там таких чудес нет.

 
Aleksei Stepanenko:

Начинает работать после прихода тика по текущей паре. Не знаю почему так, работаю в МТ4 там таких чудес нет.

О каком приходе именно речь идет(не совсем понял)?
Я навесил на график час назад - тиков было с того момента не мало )
(но ничего не прорисовывается)