А обойти можно дополнительной проверкой: 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
Ну если быть дотошным, то при увеличении таймфрейма ошибка выскакивает в строке
Код
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)
Просто основной код индикатора использует служебную функцию, которая пользуется вашими индикаторам
в этом смысл
полный код:
Код
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
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
Дело не в том, что мне заняться нечем. Просто основной код индикатора использует служебную функцию, которая пользуется вашими индикаторами. Отсюда такая вложенность.
К вашему индикатору претензий нет. Проблема в том, что вызывая его из промежуточной функции что-то происходит такое, что ошибка появляется. Словно утечка какая-то или некорректная инициализация глобальных переменных. Попробую воспроизвести ситуацию снова на чистом индикаторе.
Добрый день! Столкнулся с такой проблемой, которую не могу обойти никакими условиями. Скорее всего каких-то нюансов не учитываю.
Имеется индикатор, используемый поставляемый вами 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)
Аналогично и с другими используемыми индиктаорами. В чем может быть проблема и как от неё уйти?
function CMO_trends()
local FCMO = CMO()
local dsCMO = {Val = {}, Min = {}, Max = {}}
return function (I, Fsettings, ds)
local CMO_upTrend, CMO_downTrend
local mn = #dsCMO.Min
local mx = #dsCMO.Max
dsCMO.Val[I] = FCMO(I, Fsettings, ds) -- считаем CMO
if I > Settings.CMO_Period and dsCMO.Val[I] ~= nil and dsCMO.Val[I-1] ~= nil and dsCMO.Val[I-2] ~= nil then
if dsCMO.Val[I-1] > dsCMO.Val[I] and dsCMO.Val[I-1] > dsCMO.Val[I-2] then
dsCMO.Max[mx+1] = dsCMO.Val[I-1] -- определяем очередной максимум CMO
CMO_downTrend = mx>0 and dsCMO.Max[mx+1] < dsCMO.Max[mx] -- если максимум ниже предыдущего - нисходящий тренд
end
if dsCMO.Val[I-1] < dsCMO.Val[I] and dsCMO.Val[I-1] < dsCMO.Val[I-2] then
dsCMO.Min[mn+1] = dsCMO.Val[I-1] -- определяем очередной минимум CMO
CMO_upTrend = mn>0 and dsCMO.Min[mn+1] > dsCMO.Min[mn] -- если минимум выше предыдущего - восходящий тренд
end
end
return CMO_upTrend and 10 or nil, CMO_downTrend and -10 or nil
end
end
function CMO_trends()
local FCMO = CMO()
local dsCMO = {Val = {}, Min = {}, Max = {}}
return function (I, Fsettings, ds)
local CMO_upTrend, CMO_downTrend
local mn = #dsCMO.Min
local mx = #dsCMO.Max
dsCMO.Val[I] = FCMO(I, Fsettings, ds) -- считаем CMO
if I > Settings.CMO_Period and dsCMO.Val[I] ~= nil and dsCMO.Val[I-1] ~= nil and dsCMO.Val[I-2] ~= nil then
if dsCMO.Val[I-1] > dsCMO.Val[I] and dsCMO.Val[I-1] > dsCMO.Val[I-2] then
dsCMO.Max[mx+1] = dsCMO.Val[I-1] -- определяем очередной максимум CMO
CMO_downTrend = mx>0 and dsCMO.Max[mx+1] < dsCMO.Max[mx] -- если максимум ниже предыдущего - нисходящий тренд
end
if dsCMO.Val[I-1] < dsCMO.Val[I] and dsCMO.Val[I-1] < dsCMO.Val[I-2] then
dsCMO.Min[mn+1] = dsCMO.Val[I-1] -- определяем очередной минимум CMO
CMO_upTrend = mn>0 and dsCMO.Max[mn+1] < dsCMO.Max[mn] -- если минимум ниже предыдущего - восходящий тренд
end
end
return CMO_upTrend and 10 or nil, CMO_downTrend and -10 or nil
end
end
в этом смысле никак не могу осознать расчет SMA. Получается таблица sum накапливает данные с каждым вызовом?
Код
--[[Simple Moving Average (SMA)
SMA = sum(Pi) / n]]
function F_SMA()
local sum = {}
local it = {p=0, l=0}
return function (I, P, VT, ds)
if I == 1 then
sum = {}
it = {p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={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)
sum[Ip] = (sum[Ipp] or 0) + GetValueEX(it.p,VT,ds)
if it.l >= P then
return (sum[Ip] - (sum[Ippp] or 0)) / P
end
end
return nil
end
end
Читал, но не в полной мере осознал. Спасибо за ответ. Т.е. в моем случае нужно будет циклом от 1 до I просчитать CMO, поместить результаты в массив и работать далее с ним как с источником данных?
function CMOExtremum ()
local CMOlenght = 20
local FCMO = CMO()
return function (I, Fsettings, ds)
local CMOValues = {}
CMOValues[ 1 ] = FCMO(I, Fsettings, ds)
CMOValues[ 2 ] = FCMO(I + 1 , Fsettings, ds)
return CMOValues[ 2 ]
end
end
Добрый день! Для расчета индикатора мне нужны пара десятков последних значений индикатора CMO, который я рассчитываю в этом же файле.
Я предположил, что для этого мне нужно создать массив, в который вызвать функцию СМО для значений индекса от I до I-20 Но получается какая-то ерунда. Т.е. если смотреть код выше: если возвращать CMOValues[1], то выход индикатора совпадает с СМО Ожидается, что возврат CMOValues[2] даст ту же картину, но смещенную на один бар назад. Но нет. Получается совсем другая кривая.
Предположу, что нужно задействовать Squeeze и GetValueEX, не предположу как. Подскажите, пожалуйста, как решить задачу эффективно?
Для понимания опишу постановку. При возникновении экстремума на CMO
Код
if CMOValues[ 2 ] > CMOValues[ 1 ] and CMOValues[ 2 ] > CMOValues[ 3 ] then
maxCMO1 = CMOValues[ 2 ]
end
if CMOValues[ 2 ] < CMOValues[ 1 ] and CMOValues[ 2 ] < CMOValues[ 3 ] then
minCMO1 = CMOValues[ 2 ]
end
найти ближайший того же типа (мин, макс) и сравнить их значения. В зависимости от их соотношения выдать сигнал
function CMOExtremum()
local CMOlenght = 20
local FCMO = CMO()
return function (I, Fsettings, ds)
local CMOValues = {}
CMOValues[1] = FCMO(I, Fsettings, ds)
CMOValues[2] = FCMO(I+1, Fsettings, ds)
return CMOValues[2]
end
end
Добрый день! Для расчета индикатора мне нужны пара десятков последних значений индикатора CMO, который я рассчитываю в этом же файле.
Я предположил, что для этого мне нужно создать массив, в который вызвать функцию СМО для значений индекса от I до I-20 Но получается какая-то ерунда. Т.е. если смотреть код выше: если возвращать CMOValues[1], то выход индикатора совпадает с СМО Ожидается, что возврат CMOValues[2] даст ту же картину, но смещенную на один бар назад. Но нет. Получается совсем другая кривая.
Предположу, что нужно задействовать Squeeze и GetValueEX, не предположу как. Подскажите, пожалуйста, как решить задачу эффективно?
Для понимания опишу постановку. При возникновении экстремума на CMO
Код
if CMOValues[2] > CMOValues[1] and CMOValues[2] > CMOValues[3] then
maxCMO1 = CMOValues[2]
end
if CMOValues[2] < CMOValues[1] and CMOValues[2] < CMOValues[3] then
minCMO1 = CMOValues[2]
end
найти ближайший того же типа (мин, макс) и сравнить их значения. В зависимости от их соотношения выдать сигнал