Обращение к индикатору с разными параметрами.

Страницы: 1
RSS
Обращение к индикатору с разными параметрами.
 
Здравсвтуйте.
Вопрос по индикатору EMA, описанному в примере по ссылке. Все что описано работает. Но в моей задаче, я вычисляю среднюю EMA по нескольким периодам. Пример:
averEMA = (EMA(index, 3) + EMA(index, 4) + EMA(index, 5)) / 3.
Так как это функция, при расчете EMA с периодом 4 я затираю значение рассчитаной ЕМА3, на этой же итерации. А оно будет нужно на следующем баре. Код индикатора:
Код
 Settings= {
   Name = "averEMA",
   startPeriod   = 3,
   endPeriod   = 6,
   line =
   {
      {
         Name      = "AverEMA",
         Color      = RGB(90, 110, 200),
         Type      = TYPE_LINE,
         Width      = 1
      }
   }
}
   
function Init()
   return 1
end

function OnCalculate(index)   
   local s
   
   if index == 1 then
      EMA   = _EMA()
      
      line   = {}
   end

   s = 0
   for i = Settings.startPeriod, Settings.endPeriod do
      local k
      k = EMA(index, i)

      s = s + k
   end
      
   if index <= Settings.endPeriod then
      
      line[index] = 0   
      
   else         
      line[index] = s / (Settings.startPeriod - (Settings.endPeriod - 1))
   end
   
   message("---- message 1 -----")
   message("EMA3 = "..tostring(EMA(index, 3)))
   --message("---- message 2 -----")
   --message("EMA3 = "..tostring(EMA(index, 3))..", EMA4 = "..tostring(EMA(index, 4)))
   
   return line[index]

end
function _MA()
   local cache={}
   
   return function(iIndex, iPeriod)
      local sum      = 0
      local p         = 0
      local period   = iPeriod      
      local index      = iIndex
      local result   = 0
      local str      = ""
      
      if index == 1 then
         cache = {}
      end
      
      if index < period then
         cache[index] = 0
         return 0
      end
      
      for i = index - period + 1, index do
         sum = sum + C(i)
      end
      
      result = sum / period
      
      cache[index] = result
      
      return result
      
   end
end 

function _EMA()
   local cache={}
   local MA = _MA()
   
   return function(ind, _p, kk)
      local n = 0
      local p = 0
      local period = _p
      local index = ind
      local k = kk or 2/(period + 1)      

      if index == 1 then
         cache = {}
      end
      
      if index <= period then
         cache[index] = 0
         
         return 0
      end
      
      if cache[index - 1] == 0
         or cache[index - 1] == nil then
         
         p = MA(index - 1, period)
      else
         p = cache[index - 1]
      end
      
      n = C(index) * k + (1-k)*p
      
      cache[index] = n
      
      return n
   end
end 
Результаты теста можно увидеть на скринах. В коде изменяется только строка вывода сообщения. Первый вариант: Одна строка содержит только ЕМА3 и она выводится корректно. И второй вариант, когда выводится результата вычисления ЕМА3 и ЕМА4. И в этом случае ЕМА3 корректна только на первой итерации, дальше она считается не верно. Как такого избежать?
 
Здравствуйте,
Особенность индикатора EMA в том что для его расчета требуется знать свое предыдущее значение.
В нашем примере для этого используется механизм замыканий.
Так как для разных периодов Вы используете одну функцию, то происходит пересечения, когда функция с одним периодом берет предыдущее значение из функции с другим периодом.
Для решения проблемы, используйте две разные функции EMA
  if index == 1 then
EMA3   = _EMA()
EMA4   = _EMA()
line   = {}
  end

и далее по коду, для разных периодов используйте разные функции
 
То, что вы описали - понятно. Но предложенный вами вариант не подходит. Пользователь в параметрах указывает интервал, за который будут считаться средние. Т.е. я заранее не знаю, сколько функций создавать. Есть ли другой выход? Или возможно как то создать динамический массив из функций?
 
Цитата
Дмитрий Минеев пишет:
То, что вы описали - понятно. Но предложенный вами вариант не подходит. Пользователь в параметрах указывает интервал, за который будут считаться средние. Т.е. я заранее не знаю, сколько функций создавать. Есть ли другой выход? Или возможно как то создать динамический массив из функций?
Все зависит от Вашего воображения.
Перепишите пример так чтобы он запоминал последнее значение индикатора в переменной.
 
Цитата
Sergey Gorokhov пишет:
Цитата
Дмитрий Минеев пишет:
То, что вы описали - понятно. Но предложенный вами вариант не подходит. Пользователь в параметрах указывает интервал, за который будут считаться средние. Т.е. я заранее не знаю, сколько функций создавать. Есть ли другой выход? Или возможно как то создать динамический массив из функций?
Все зависит от Вашего воображения.
Перепишите пример так чтобы он запоминал последнее значение индикатора в переменной.
Как это поможет решению моего вопроса?
 
Цитата
Дмитрий Минеев пишет:
Как это поможет решению моего вопроса?
переписать пример так, чтобы он генерировал функции, кажется сложнее, чем переписать так, чтобы была глобальная таблица, в которой хранились бы нужные данные о предыдущих значениях. Для каждого периода своя ячейка в таблице.
 
Цитата
Sergey Gorokhov пишет:
Цитата
Дмитрий Минеев пишет:
Как это поможет решению моего вопроса?
переписать пример так, чтобы он генерировал функции, кажется сложнее, чем переписать так, чтобы была глобальная таблица, в которой хранились бы нужные данные о предыдущих значениях. Для каждого периода своя ячейка в таблице.
Ок. Спасибо. Попробую.
 
Код
Settings= {
   Name = "averEMA",
   startPeriod   = 3,
   endPeriod   = 6,
   line =
   {
      {
         Name      = "AverEMA",
         Type      = TYPE_LINE,
         Width      = 2
      }
   }
}
   
function Init()
   return 1
end

function average(_start, _end)
   local sum=0
   for i = _start, _end do
      sum=sum+C(i)
   end
   return sum/(_end-_start+1)
end

function cached_EMA(__period, __k)
   local cache={}
   local period = __period
   local k = __k or 2/(period+1)
   return function(ind)
      local n = 0
      local p = 0
      --local period = _p
      local index = ind
      if index == 1 then
         cache = {}
      end
      if index < period then
         cache[index] = average(1,index)
         return nil
      end
      p = cache[index-1] or C(index)
      n = k*C(index)+(1-k)*p
      cache[index] = n
      return n
   end
end

function OnCalculate(index)   
   local s=0
   if index == 1 then
      line   = {}
      for i=Settings.startPeriod, Settings.endPeriod do
         line[i - Settings.startPeriod + 1] = cached_EMA(i)
      end
   end

   s = 0
   for i = 1, #line do
      local res = line[i](index)
      if res == nil then
         return nil
      else
         s = s + res
      end
   end
   
   return s/#line
end

 
 
Michael Bulychev, спасибо за развернутый ответ. Но, в вашем случае призойдет то же самое, что и у меня:
На первой итерации, при расчете EMA(3) на 2 баре (например), мы получим корректное значение, но рассчет ЕМА(4) на том же баре затрет кешированное значение для ЕМА(3) и на следующей итерации оно посчитается не коррекно.
В вашем методе реализиции необходимо в функцию ЕМА() передавать переменную Line и поменять вычисление на:
Код
p = line[index-Settings.startPeriod] or C(index)
   n = k*C(index)+(1-k)*p 
вот так должно заработать.
 
алгоритм - "ниасилил". Вам надо смотреть в сторону ускоренного алгоритма вычисления скользящих средних. в интернете - хоть и мало но, есть по этому поводу информация. применение же стандартного подхода, ничего кроме тормозов не даст, а уж тем более его использование в скриптах индикаторов.
 
Цитата
Дмитрий Минеев пишет:
Michael Bulychev , спасибо за развернутый ответ. Но, в вашем случае призойдет то же самое, что и у меня:
На первой итерации, при расчете EMA(3) на 2 баре (например), мы получим корректное значение, но рассчет ЕМА(4) на том же баре затрет кешированное значение для ЕМА(3) и на следующей итерации оно посчитается не коррекно.
В вашем методе реализиции необходимо в функцию ЕМА() передавать переменную Line и поменять вычисление на:
Код
 p = line[index-Settings.startPeriod] or C(index)
   n = k*C(index)+(1-k)*p 
 
вот так должно заработать.
Добрый день.
не затрет. Для каждого такого замыкания будет создана своя копия локальных данных cache{} и параметра period. Вот пример попроще:
Код
function counter_from(i)
   local x=i
   return function ()
      x=x+1
      return x-1
   end
end

c1 = counter_from(1)
c2 = counter_from(100)
for i=1, 10 do
   print("c1 = " .. c1() .. ", c2 = " .. c2())
end
 
результат:
Код
c1 = 1, c2 = 100
c1 = 2, c2 = 101
c1 = 3, c2 = 102
c1 = 4, c2 = 103
c1 = 5, c2 = 104
c1 = 6, c2 = 105
c1 = 7, c2 = 106
c1 = 8, c2 = 107
c1 = 9, c2 = 108
c1 = 10, c2 = 109
 
 
Michael Bulychev,
Спасибо. Не внимательно прочитал ваш код. Не увидел, что вы создаете массив из функций. Хорошее, рабочее решение.
Страницы: 1
Читают тему
Наверх