Добрый день! Столкнулся с такой проблемой, которую не могу обойти никакими условиями. Скорее всего каких-то нюансов не учитываю.
Имеется индикатор, используемый поставляемый вами 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)
Аналогично и с другими используемыми индиктаорами. В чем может быть проблема и как от неё уйти?
К вашему индикатору претензий нет. Проблема в том, что вызывая его из промежуточной функции что-то происходит такое, что ошибка появляется. Словно утечка какая-то или некорректная инициализация глобальных переменных. Попробую воспроизвести ситуацию снова на чистом индикаторе.
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
Дело не в том, что мне заняться нечем. Просто основной код индикатора использует служебную функцию, которая пользуется вашими индикаторами. Отсюда такая вложенность.
Kaavan, Не видим причин делать вложенные функции внутри вложенных функций внутри вложенной функции. Зачем? Каков смысл? Если хотите разобраться, выкладываейте полный код а не часть (или это и есть полный код? тогда проблема в этом т.к. нет нужных функций)
Просто основной код индикатора использует служебную функцию, которая пользуется вашими индикаторам
в этом смысл
полный код:
Код
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 подобное может повториться.
Ну если быть дотошным, то при увеличении таймфрейма ошибка выскакивает в строке
Код
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)
А обойти можно дополнительной проверкой: 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
Добавлю свои пять копеек к решению проблемы. ----------------------------------------- Обычно такая проблема возникает, если есть массивы, которые определяются лишь при старте индикатора. В результате, при смене тайма, они остаются не пустыми. В итоге нарушается индексация и возникают отсутствующие элементы ------------------------- Проблему решаем так: ---------------------------- Создаем специальную функцию инициализации всех переменных индикатора. ----------------------- Эту функцию вызываем в двух местах - в Init() - здесь можно и не вызывать, но делаю это, так как Init как бы для этого создана. Вызывается в индикаторе один раз при запуске и не влияет на скорость исполнения. и в onCalculate() при индексе равном 1 Например так: -------------------------------- function OnCalculate(k) if k==1 then -- вызываем функцию инициализации end -------------------------- Если будете выполнять данное правило, то проблем при переключении тайма не будет.