Добрый день! Столкнулся с такой проблемой, которую не могу обойти никакими условиями. Скорее всего каких-то нюансов не учитываю.
Имеется индикатор, используемый поставляемый вами CMO()
Код
Settings = {
Name = "*Kaavan Signals TEST",
CMO_Period = 14,
line = {
{
Name = "CMO",
Color = RGB(0, 255, 0),
Type = TYPE_POINT,
Width = 3
}
}
}
function Init()
func = GetSignals()
return #Settings.line
end
function OnCalculate(Index)
return func(Index, Settings)
end
function GetSignals()
local FCMO = CMO() --Chande Momentum Oscillator ("CMO")
local dsCMO = {Val = {}, Min = {}, Max = {}}
return function (I, Fsettings, ds)
if I < Fsettings.CMO_Period then
return nil
else
dsCMO.Val[I] = FCMO(I, Fsettings, ds) -- считаем CMO
end
end
end
------------------------------
function CMO() --Chande Momentum Oscillator ("CMO")
local sum={}
local sum2={}
local it = {pp=0, p=0, l=0}
return function (I, Fsettings, ds)
local Fsettings=(Fsettings or {})
local P = (Fsettings.CMO_Period or 14)
local VT = (Fsettings.CMO_VType or CLOSE)
if (P>0) then
if I == 1 then
sum={}
sum2={}
it = {pp=0, p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={pp=it.p, p=I, l=it.l+1} end
local Ip,Ipp,Ippp = Squeeze(it.l,P),Squeeze(it.l-1,P),Squeeze(it.l-P,P)
if it.l > 1 then
local diff = GetValueEX(it.p,VT,ds) - GetValueEX(it.pp,VT,ds)
if diff > 0 then
sum[Ip] = (sum[Ipp] or 0) + diff
sum2[Ip] = (sum2[Ipp] or 0)
elseif diff < 0 then
sum[Ip] = (sum[Ipp] or 0)
sum2[Ip] = (sum2[Ipp] or 0) - diff
elseif diff == 0 then
sum[Ip] = (sum[Ipp] or 0)
sum2[Ip] = (sum2[Ipp] or 0)
end
end
if it.l > P then
local CMO1 = sum[Ip]-(sum[Ippp] or 0)
local CMO2 = sum2[Ip]-(sum2[Ippp] or 0)
return (CMO1 - CMO2) / (CMO1 + CMO2) * 100
end
end
end
return nil
end
end
Отрабатывает без ошибок, но при увеличении таймфрейма выдает следующую ошибку:attempt to perform arithmetic on a nil value на строке 55:
Код
local diff = GetValueEX(it.p,VT,ds) - GetValueEX(it.pp,VT,ds)
Аналогично и с другими используемыми индиктаорами. В чем может быть проблема и как от неё уйти?
Пользователь
Сообщений: Регистрация: 31.07.2020
10.08.2020 03:37:33
При уменьшении таймфрейма такой ошибки не возникает
Пользователь
Сообщений: Регистрация: 23.01.2015
10.08.2020 06:21:48
Цитата
Kaavan написал: Имеется индикатор, используемый поставляемый вами CMO()
Это не правда, у нас другой код в индикаторе. На такая проблема не возникает.
Пользователь
Сообщений: Регистрация: 31.07.2020
10.08.2020 13:49:05
К вашему индикатору претензий нет. Проблема в том, что вызывая его из промежуточной функции что-то происходит такое, что ошибка появляется. Словно утечка какая-то или некорректная инициализация глобальных переменных. Попробую воспроизвести ситуацию снова на чистом индикаторе.
Пользователь
Сообщений: Регистрация: 31.07.2020
10.08.2020 14:02:01
Получилось. Двойной вызов.
Вот такая надстройка над вашим индикатором
Код
Settings = {
Name = "*CMO (TEST)",
Period = 14,
VType = "Close", --(Open, High, Low, Close, Volume, Median, Typical, Weighted, Difference)
line = {{
Name = "Horizontal line (top)",
Type = TYPE_LINE,
Color = RGB(140, 140, 140)
},
{
Name = "Horizontal line (bottom)",
Type = TYPE_LINE,
Color = RGB(140, 140, 140)
},
{
Name = "CMO",
Type = TYPE_LINE,
Color = RGB(221, 44, 44)
}
},
Round = "off",
Multiply = 1,
Horizontal_line="50"
}
function Init()
func = Test1()
return #Settings.line
end
function OnCalculate(Index)
local Out = ConvertValue(Settings, func(Index, Settings))
local HL = tonumber(Settings.Horizontal_line)
if HL then
return HL,-HL,Out
else
return nil,nil,Out
end
end
function Test1()
local FTest = Test2()
return function (I, Fsettings, ds)
return FTest(I, Fsettings, ds)
end
end
function Test2()
local FCMO = CMO()
local dsCMO = {Val = {}, Min = {}, Max = {}}
return function (I, Fsettings, ds)
dsCMO.Val[I] = FCMO(I, Fsettings, ds)
return dsCMO.Val[I]
end
end
дает подобную ошибку ТОЛЬКО ПРИ УВЕЛИЧЕНИИ ТАЙМФРЕЙМА. для строки return dsCMO.Val[I] пишет CMO_TEST.lua:54: attempt to perform arithmetic on a nil value
Дело не в том, что мне заняться нечем. Просто основной код индикатора использует служебную функцию, которая пользуется вашими индикаторами. Отсюда такая вложенность.
Пользователь
Сообщений: Регистрация: 31.07.2020
10.08.2020 14:02:50
При этом построение индикатора выполняется корректно
Пользователь
Сообщений: Регистрация: 23.01.2015
10.08.2020 14:15:24
Kaavan, Не видим причин делать вложенные функции внутри вложенных функций внутри вложенной функции. Зачем? Каков смысл? Если хотите разобраться, выкладываейте полный код а не часть (или это и есть полный код? тогда проблема в этом т.к. нет нужных функций)
Пользователь
Сообщений: Регистрация: 31.07.2020
10.08.2020 14:33:34
Цитата
Просто основной код индикатора использует служебную функцию, которая пользуется вашими индикаторам
в этом смысл
полный код:
Код
Settings = {
Name = "*CMO (TEST)",
Period = 14,
VType = "Close", --(Open, High, Low, Close, Volume, Median, Typical, Weighted, Difference)
line = {{
Name = "Horizontal line (top)",
Type = TYPE_LINE,
Color = RGB(140, 140, 140)
},
{
Name = "Horizontal line (bottom)",
Type = TYPE_LINE,
Color = RGB(140, 140, 140)
},
{
Name = "CMO",
Type = TYPE_LINE,
Color = RGB(221, 44, 44)
}
},
Round = "off",
Multiply = 1,
Horizontal_line="50"
}
function Init()
func = Test1()
return #Settings.line
end
function OnCalculate(Index)
local Out = ConvertValue(Settings, func(Index, Settings))
local HL = tonumber(Settings.Horizontal_line)
if HL then
return HL,-HL,Out
else
return nil,nil,Out
end
end
function Test1()
local FTest = Test2()
return function (I, Fsettings, ds)
return FTest(I, Fsettings, ds)
end
end
function Test2()
local FCMO = CMO()
local dsCMO = {Val = {}, Min = {}, Max = {}}
return function (I, Fsettings, ds)
dsCMO.Val[I] = FCMO(I, Fsettings, ds)
return dsCMO.Val[I]
end
end
function CMO() --Chande Momentum Oscillator ("CMO")
local sum={}
local sum2={}
local it = {pp=0, p=0, l=0}
return function (I, Fsettings, ds)
local Fsettings=(Fsettings or {})
local P = (Fsettings.Period or 14)
local VT = (Fsettings.VType or CLOSE)
if (P>0) then
if I == 1 then
sum={}
sum2={}
it = {pp=0, p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={pp=it.p, p=I, l=it.l+1} end
local Ip,Ipp,Ippp = Squeeze(it.l,P),Squeeze(it.l-1,P),Squeeze(it.l-P,P)
if it.l > 1 then
local diff = GetValueEX(it.p,VT,ds) - GetValueEX(it.pp,VT,ds)
if diff > 0 then
sum[Ip] = (sum[Ipp] or 0) + diff
sum2[Ip] = (sum2[Ipp] or 0)
elseif diff < 0 then
sum[Ip] = (sum[Ipp] or 0)
sum2[Ip] = (sum2[Ipp] or 0) - diff
elseif diff == 0 then
sum[Ip] = (sum[Ipp] or 0)
sum2[Ip] = (sum2[Ipp] or 0)
end
end
if it.l > P then
local CMO1 = sum[Ip]-(sum[Ippp] or 0)
local CMO2 = sum2[Ip]-(sum2[Ippp] or 0)
return (CMO1 - CMO2) / (CMO1 + CMO2) * 100
end
end
end
return nil
end
end
SMA,MMA,EMA,WMA,SMMA,VMA = "SMA","MMA","EMA","WMA","SMMA","VMA"
OPEN,HIGH,LOW,CLOSE,VOLUME,MEDIAN,TYPICAL,WEIGHTED,DIFFERENCE,ANY = "O","H","L","C","V","M","T","W","D","A"
function CandleExist(I,ds)
return (type(C)=="function" and C(I)~=nil) or
(type(ds)=="table" and (ds[I]~=nil or (type(ds.Size)=="function" and (I>0) and (I<=ds:Size()))))
end
function Squeeze(I,P)
return math.fmod(I-1,P+1)
end
function ConvertValue(T,...)
local function r(V, R)
if R and string.upper(R)== "ON" then R=0 end
if V and tonumber(R) then
if V >= 0 then return math.floor(V * 10^R + 0.5) / 10^R
else return math.ceil(V * 10^R - 0.5) / 10^R end
else return V end
end
local arg = {...}
arg.n = select('#', ...)
if arg.n > 0 then
for i = 1, arg.n do
arg[i]=arg[i] and r(arg[i] * ((T and T.Multiply) or 1), (T and T.Round) or "off")
end
return unpack(arg)
else return nil end
end
function GetValueEX(I,VT,ds)
VT=(VT and string.upper(string.sub(VT,1,1))) or ANY
if VT == OPEN then --Open
return (O and O(I)) or (ds and ds:O(I))
elseif VT == HIGH then --High
return (H and H(I)) or (ds and ds:H(I))
elseif VT == LOW then --Low
return (L and L(I)) or (ds and ds:L(I))
elseif VT == CLOSE then --Close
return (C and C(I)) or (ds and ds:C(I))
elseif VT == VOLUME then --Volume
return (V and V(I)) or (ds and ds:V(I))
elseif VT == MEDIAN then --Median
return ((GetValueEX(I,HIGH,ds) + GetValueEX(I,LOW,ds)) / 2)
elseif VT == TYPICAL then --Typical
return ((GetValueEX(I,MEDIAN,ds) * 2 + GetValueEX(I,CLOSE,ds))/3)
elseif VT == WEIGHTED then --Weighted
return ((GetValueEX(I,TYPICAL,ds) * 3 + GetValueEX(I,OPEN,ds))/4)
elseif VT == DIFFERENCE then --Difference
return (GetValueEX(I,HIGH,ds) - GetValueEX(I,LOW,ds))
else --Any
return (ds and ds[I])
end
return nil
end
Не убедительно. Просто если Вы не знаете как сделать по другому, это не значит что нужно делать вслепую как у нас. тем более несколько глубин вложений одно в другом Вот мы например сделали вложения в индикаторах, чтобы можно было делать несколько функций расчета индикатора.
local FCMO1 = CMO() local FCMO2 = CMO() local FCMO3 = CMO() и т.п.
У Вас же абсолютно бессмысленное вложение во вложении во вложении. Почему теряются переменные, не понятно, возможно какая-то особенность lua, но совершенно точно сам индикатор тут не причем ибо даже на A+B подобное может повториться.
Пользователь
Сообщений: Регистрация: 31.07.2020
10.08.2020 15:53:06
Цитата
У Вас же абсолютно бессмысленное вложение во вложении во вложении.
услышал. Действительно еще недопонимаю нюансов - поэтому и пишу. Попробую упростить.
Цитата
local FCMO1 = CMO() local FCMO2 = CMO() local FCMO3 = CMO()
это для последовательного вызова? А трижды FCMO1 вызвать нельзя было?
Пользователь
Сообщений: Регистрация: 23.01.2015
10.08.2020 19:40:57
Цитата
Kaavan написал: это для последовательного вызова? А трижды FCMO1 вызвать нельзя было?
это для расчета индикаторов с разными параметрами. например если надо сделать три (или сколько нужно) индикатора и у каждого свой период.
Пользователь
Сообщений: Регистрация: 31.07.2020
13.08.2020 15:24:58
Ну если быть дотошным, то при увеличении таймфрейма ошибка выскакивает в строке
Код
GetValueEX(it.pp,VT,ds)
т.к. it.pp хранит номер предыдущего индекса, а при первом прогоне этого номер старшей свечи предыдущего графика. При увеличении таймфрейма количество свечей уменьшается, отсюда идет обращение к несуществующему элементу массива.
Отсюда вопрос: Есть ли способ обнулить переменные блока ниже?
Код
function CMO() --Chande Momentum Oscillator ("CMO")
local sum={}
local sum2={}
local it = {pp=0, p=0, l=0}
return function (I, Fsettings, ds)
Пользователь
Сообщений: Регистрация: 31.07.2020
13.08.2020 15:35:47
А обойти можно дополнительной проверкой: if it.p < it.pp then it.pp = it.p-1 end Спасибо за внимание)
Код
function CMO(I, Fsettings, ds) --Chande Momentum Oscillator ("CMO")
local sum={}
local sum2={}
local it = {pp=0, p=0, l=0}
return function (I, Fsettings, ds)
local Fsettings=(Fsettings or {})
local P = (Fsettings.CMO_Period or 14)
local VT = (Fsettings.CMO_VType or CLOSE)
PrintDbgStr(P)
if (P>0) then
if I == 1 then
sum={}
sum2={}
it = {pp=0, p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={pp=it.p, p=I, l=it.l+1} end
if it.p < it.pp then it.pp = it.p-1 end
Пользователь
Сообщений: Регистрация: 31.07.2020
13.08.2020 15:43:53
Проблема устранена. Аналогичная проверка потребуется и в других индикаторах, где используется подобная конструкция. Для PSAR даже дважды:
Код
it={ppp=it.pp, pp=it.p, p=I, l=it.l+1}
if it.p < it.pp then it.pp = it.p-1 end -- van
if it.pp < it.ppp then it.ppp = it.pp-1 end -- van
Пользователь
Сообщений: Регистрация: 30.01.2015
20.08.2020 07:22:53
Добавлю свои пять копеек к решению проблемы. ----------------------------------------- Обычно такая проблема возникает, если есть массивы, которые определяются лишь при старте индикатора. В результате, при смене тайма, они остаются не пустыми. В итоге нарушается индексация и возникают отсутствующие элементы ------------------------- Проблему решаем так: ---------------------------- Создаем специальную функцию инициализации всех переменных индикатора. ----------------------- Эту функцию вызываем в двух местах - в Init() - здесь можно и не вызывать, но делаю это, так как Init как бы для этого создана. Вызывается в индикаторе один раз при запуске и не влияет на скорость исполнения. и в onCalculate() при индексе равном 1 Например так: -------------------------------- function OnCalculate(k) if k==1 then -- вызываем функцию инициализации end -------------------------- Если будете выполнять данное правило, то проблем при переключении тайма не будет.