Сдвигающийся с каждым тиком буфер

 

Подскажите, как заставить эту машину работать?
Каждый когда приходит тик, необходимо сдвигать весь буфер на 1 назад и в первый элемент ставить значение Bid+size*_Point

То есть это должна быть кривая линия которая обновляется на каждом тике

Но рисуется непонятно что, такое чувство что это работает на каждый бар, а не тик

Ниже пример кода

#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
//---- plot 
#property indicator_label1  "myInd"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Blue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

double buff[], Bid;
input int lenght = 50, size = 5;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping

   ArrayResize(buff, 50);
   ArrayInitialize(buff, 0);
   SetIndexBuffer(0, buff, INDICATOR_DATA);
   //--- установим метку для отображения в DataWindow
   PlotIndexSetString(0,PLOT_LABEL,"myInd");   
//--- установим имя для показа в отдельном подокне и во всплывающей подсказке
   IndicatorSetString(INDICATOR_SHORTNAME,"myInd");
//--- укажем точность отображения значений индикатора
   IndicatorSetInteger(INDICATOR_DIGITS, _Point);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//---
   Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   for(int i = lenght-1; i>0; i--){
      buff[i] = buff[i-1];
   }
   buff[0] = Bid+size*_Point;
   


   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
 
Roman Sharanov:

Подскажите, как заставить эту машину работать?
Каждый когда приходит тик, необходимо сдвигать весь буфер на 1 назад и в первый элемент ставить значение Bid+size*_Point

То есть это должна быть кривая линия которая обновляется на каждом тике

Но рисуется непонятно что, такое чувство что это работает на каждый бар, а не тик

Ниже пример кода

Код неверный, нельзя задавать размер индикаторного буфера, это прерогатива рантайма МТ4/5. Посмотрите, как устроены стандартные индикаторы.

ArrayResize(buff, 50); // ошибка
ArrayInitialize(buff, 0);

**

 
https://www.mql5.com/ru/code/89
Ticks
Ticks
  • голосов: 38
  • 2010.03.24
  • MetaQuotes Software Corp.
  • www.mql5.com
Простой пример индикатора для отображения тиков в отдельном подокне. Используются стандартные индикаторные буфера, которые сдвигаются на каждом тике с помощью функции PlotIndexSetInteger().
 
Rashid Umarov:
https://www.mql5.com/ru/code/89

Спасибо за ссылку. Первая мысль у меня была попробовать решить эту задачу с помощью ArrayCopy. плохая мысль.

 
Alexey Viktorov:

Спасибо за ссылку. Первая мысль у меня была попробовать решить эту задачу с помощью ArrayCopy. плохая мысль.

Это единственно правильная мысль.

Мало того, что PLOT_SHIFT не документирован, так еще и представленный индикатор будет создавать видимость рабочего только некоторое время.

 
fxsaber:

Это единственно правильная мысль.

Мало того, что PLOT_SHIFT не документирован, так еще и представленный индикатор будет создавать видимость рабочего только некоторое время.

Когда и если будет необходимость, проверю оба варианта. Спасибо за мнение.

 

На мой взгляд, копирование массива на каждом тике - неверное направление.

В теории-то все правильно, но слишком медленно.

Я бы сделал указатель, и на каждом тике бы наращивал его, с добавлением значения. А доступ к значениям - сделал бы "обратным". Ну и обернул бы все в класс, чтобы "снаружи" это выглядело, как буффер со сдвигом на каждом тике.

 
Georgiy Merts:

На мой взгляд, копирование массива на каждом тике - неверное направление.

В теории-то все правильно, но слишком медленно.

Я бы сделал указатель, и на каждом тике бы наращивал его, с добавлением значения. А доступ к значениям - сделал бы "обратным". Ну и обернул бы все в класс, чтобы "снаружи" это выглядело, как буффер со сдвигом на каждом тике.

Попробуйте сделать. Результат будет интересен даже без ООП.

 
fxsaber:

Попробуйте сделать. Результат будет интересен даже без ООП.

Да чего там делать ? Есть у меня все это.

Вот, пожалуйста, .MQH:

/*
CQueueT - класс-шаблон, представляющий собой очередь
Элементы добавляются в хвост очереди, и могут быть удалены с головы очереди.
Если при добавлении происходит переполнение, то элемент с головы исчезает.
*/

#include <MyLib\Common\MyObject.mqh>

template <typename T>
class CQueueT:public CMyObject
{
protected:
   T     m_atObjects[];
   uint  m_uiMaxQueueSize; 
   uint  m_uiDataSize;
   int   m_iTailPointer;   // Указатель на "хвост" очереди (на последний,самый новый элемент). Число со знаком, поскольку при расчете индекса оно может быть меньше нуля.

public:
   CQueueT();
   ~CQueueT() {};

   // Функция выделяет память для очереди, и подготавливает ее к принятию первого значения.
   bool Init(uint uiMaxQueueSize);
  
   uint GetQueueSize() { return(m_uiDataSize); };
   
   // Доступ к объектам очереди
   T GetObjectFromHead(uint uiIdxFromHead = 0) const; // Возврат объекта, находящегося в очереди по индексу uiIdxFromHead от "головы" очереди (от самого старого элемента), сам объект не удаляется   
   T GetObjectFromTail(uint uiIdxFromTail = 0) const;  // Возврат объекта с хвоста очереди, сам объект не удаляется
   
   // Добавление объекта в "хвост" очереди со смещением всех предыдущих со смещением всех предыдущих к "голове" очереди.
   // Объект, для которого не оказывается места удаляется.
   // Если размер очереди увеличился - функция возвращает true. Если размер не изменился (самый старый объект был удален) - false.
   // NOTE !!! 
   // Передача просто объекта - в темплейтах не выходит, потому, в общем случае - структуры не могут передаваться по значению. 
   // Поэтому - приходится передавать по ссылке, в том числе и для простых типов.   
   bool AddToTail(T & tObject);
   
   // Удалени объекта, находящегося на голове очереди (самого старого). 
   // Если это возможно - возвращается true, и удаленный объект передается в параметр tObject
   // Если это невозможно - возвращается false.
   bool RemoveFromHead(T & tObject);
};


К нему - еще и .MQ5 файл:


#property library

#include <MyLib\DebugOrRelease\DebugSupport.mqh>
#include <MyLib\Arrays\QueueT.mqh>

template<typename T>
CQueueT::CQueueT()
{
   SetMyObjectType(MOT_QUEUE_T);
   m_uiMaxQueueSize = 0;
   m_uiDataSize = 0;
   m_iTailPointer = INT_MAX;
};

template<typename T>
bool CQueueT::Init(uint uiMaxQueueSize)
{
   ASSERT(uiMaxQueueSize > 0);
   
   int iResSize = ArrayResize(m_atObjects,uiMaxQueueSize);
   
   if(iResSize != uiMaxQueueSize)
      return(false);
      
   m_uiMaxQueueSize =  uiMaxQueueSize;
   m_uiDataSize = 0;
   m_iTailPointer = INT_MAX;
   
   return(true);  
};

template<typename T>
bool CQueueT::AddToTail(T & tObject)
{
   ASSERT(m_uiMaxQueueSize > 0);
   ASSERT(ArraySize(m_atObjects) == m_uiMaxQueueSize);

   if(m_uiDataSize == 0)
      {
      m_iTailPointer = 0;
      m_atObjects[0] = tObject; 
      m_uiDataSize = 1;
      return(true);
      };

   ASSERT(m_uiDataSize <= m_uiMaxQueueSize);
   ASSERT(m_iTailPointer < (int)m_uiMaxQueueSize);

   bool bRes = false;

   ++m_iTailPointer;
   
   if(m_iTailPointer == (int)m_uiMaxQueueSize)
      m_iTailPointer = 0;
      
   if(m_uiDataSize<m_uiMaxQueueSize)
      {
      ++m_uiDataSize;
      bRes = true;
      };

   m_atObjects[m_iTailPointer] = tObject; 

   return(bRes);
};


template<typename T>
T CQueueT::GetObjectFromHead(uint uiIdxFromHead) const
{
   ASSERT(m_uiDataSize > 0);
   ASSERT(uiIdxFromHead < m_uiDataSize);
   ASSERT(m_uiDataSize <= m_uiMaxQueueSize);
   ASSERT(m_iTailPointer < (int)m_uiMaxQueueSize);
   
   int iResPointer = m_iTailPointer-(int)m_uiDataSize+(int)uiIdxFromHead+1;
   
   if(iResPointer<0)
      iResPointer += (int)m_uiDataSize;
   else
      if(iResPointer >= (int)m_uiDataSize)
         iResPointer -= (int)m_uiDataSize;
   
   return(m_atObjects[iResPointer]);      
};

template<typename T>
T CQueueT::GetObjectFromTail(uint uiIdxFromTail) const
{
   ASSERT(m_uiDataSize > 0);
   ASSERT(uiIdxFromTail < m_uiDataSize);
   ASSERT(m_uiDataSize <= m_uiMaxQueueSize);
   ASSERT(m_iTailPointer < (int)m_uiMaxQueueSize);
   
   int iResPointer = m_iTailPointer-(int)uiIdxFromTail;
   
   if(iResPointer<0)
      iResPointer += (int)m_uiDataSize;
   
   return(m_atObjects[iResPointer]);      
};


template<typename T>
bool CQueueT::RemoveFromHead(T & tObject)
{
   ASSERT(m_uiDataSize <= m_uiMaxQueueSize);
   ASSERT(m_iTailPointer < (int)m_uiMaxQueueSize);

   if(m_uiDataSize == 0)
      return(false);

   int iHeadPointer = m_iTailPointer-(int)m_uiDataSize+1;
   
   if(iHeadPointer<0)
      iHeadPointer += (int)m_uiDataSize;

   tObject = m_atObjects[iHeadPointer]; 
   
   --m_uiDataSize;
   
   if(m_uiDataSize == 0)
      m_iTailPointer = INT_MAX;
   
   return(true);         
};

Это очередь.

Все работает - именно так, как я и предлагаю.  Добавляем в хвост очереди тики (можно прямо структуру MQLTickInfo), берем данные с головы очереди. Причем, если очередь переполняется, то исчезает элемент "с головы" (на его место просто записывается новый "хвостовой" элемент. Формально, мы добавляем в хвост элементы, и весь массив "сдвигаем вверх", к "голове". Но физически - мы дописываем лишь только новый "хвостовой" элемент, копирование всего массива при этом - не происходит.

Этот код не очень оттестирован, но работает вроде нормально. Думаю, можно рекомендовать к использованию (удалив несколько специфичных для моей библиотеки строк)

 
Georgiy Merts:

Все работает - именно так, как я и предлагаю.

Где тиковый индикатор?

 

Я про индикатор ничего не говорил. Я говорил, что копировать весь массив для сдвига представляется неразумным. Куда правильнее дописывать данные, а сдвиг осуществлять с помощью указателей. Мой класс CQueueT это и делает.

Чтобы получить индкатор - надо последнее значение из очереди (или сколько там будет надо, в зависимости от параметра prev_calculated - копировать в индикаторный массив.