Ошибка в индикаторе при увеличении таймфрейма

Страницы: 1
RSS
Ошибка в индикаторе при увеличении таймфрейма
 
Добрый день!
Столкнулся с такой проблемой, которую не могу обойти никакими условиями.
Скорее всего каких-то нюансов не учитываю.

Имеется индикатор, используемый поставляемый вами 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)
Аналогично и с другими используемыми индиктаорами.
В чем может быть проблема и как от неё уйти?
 
При уменьшении таймфрейма такой ошибки не возникает
 
Цитата
Kaavan написал:
Имеется индикатор, используемый поставляемый вами CMO()
Это не правда, у нас другой код в индикаторе.
На нашем такая проблема не возникает.
 
К вашему индикатору претензий нет.
Проблема в том, что вызывая его из промежуточной функции что-то происходит такое, что ошибка появляется. Словно утечка какая-то или некорректная инициализация глобальных переменных.
Попробую воспроизвести ситуацию снова на чистом индикаторе.
 
Получилось.
Двойной вызов.

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

local FCMO1 = CMO()
local FCMO2 = CMO()
local FCMO3 = CMO()
и т.п.

У Вас же абсолютно бессмысленное вложение во вложении во вложении.
Почему теряются переменные, не понятно, возможно какая-то особенность lua, но совершенно точно сам индикатор тут не причем ибо даже на A+B подобное может повториться.
 
Цитата
У Вас же абсолютно бессмысленное вложение во вложении во вложении.
услышал. Действительно еще недопонимаю нюансов - поэтому и пишу. Попробую упростить.

Цитата
local FCMO1 = CMO()
local FCMO2 = CMO()
local FCMO3 = CMO()
это для последовательного вызова? А трижды FCMO1 вызвать нельзя было?
 
Цитата
Kaavan написал:
это для последовательного вызова? А трижды FCMO1 вызвать нельзя было?

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