Здравсвтуйте. Вопрос по индикатору 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
и далее по коду, для разных периодов используйте разные функции
То, что вы описали - понятно. Но предложенный вами вариант не подходит. Пользователь в параметрах указывает интервал, за который будут считаться средние. Т.е. я заранее не знаю, сколько функций создавать. Есть ли другой выход? Или возможно как то создать динамический массив из функций?
Дмитрий Минеев пишет: То, что вы описали - понятно. Но предложенный вами вариант не подходит. Пользователь в параметрах указывает интервал, за который будут считаться средние. Т.е. я заранее не знаю, сколько функций создавать. Есть ли другой выход? Или возможно как то создать динамический массив из функций?
Все зависит от Вашего воображения. Перепишите пример так чтобы он запоминал последнее значение индикатора в переменной.
Дмитрий Минеев пишет: То, что вы описали - понятно. Но предложенный вами вариант не подходит. Пользователь в параметрах указывает интервал, за который будут считаться средние. Т.е. я заранее не знаю, сколько функций создавать. Есть ли другой выход? Или возможно как то создать динамический массив из функций?
Все зависит от Вашего воображения. Перепишите пример так чтобы он запоминал последнее значение индикатора в переменной.
Дмитрий Минеев пишет: Как это поможет решению моего вопроса?
переписать пример так, чтобы он генерировал функции, кажется сложнее, чем переписать так, чтобы была глобальная таблица, в которой хранились бы нужные данные о предыдущих значениях. Для каждого периода своя ячейка в таблице.
Дмитрий Минеев пишет: Как это поможет решению моего вопроса?
переписать пример так, чтобы он генерировал функции, кажется сложнее, чем переписать так, чтобы была глобальная таблица, в которой хранились бы нужные данные о предыдущих значениях. Для каждого периода своя ячейка в таблице.
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