Динамическое количество линий индикатора

Страницы: 1
RSS
Динамическое количество линий индикатора
 
Добрый день.

Т.к. в индикаторе необходимо при инициализации указывать количество возвращаемых линий, то написана такая конструкция в функции init:
Код
for i = 1, lines do
        Settings.line[i] = {}
        Settings.line[i] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
    end
Алгоритм вводит линии по мере наполнения массива линий. Сначала их может быть 20, потом уже 100 и т.д. Для оптимизации линии выводятся только с не пустым значением.

Все бы хорошо, но заметил такую особенность (версия 7.19), если не вывести хотя бы один раз все линиий с не пустым значением, то потом они не отображаются на графике, хотя функция GetValue возвращает значение. Я долго не мог понять почему у меня линии не показываются с течением времени, а если заново инициализировать индикатор (через параметры графика), то все на месте.

Пришлось сделать такой странный костыль:
Код
if index == 1 then
    tmpOutlines = {}
    for i=1,lines do --необходимо вывести все линии хотя бы один раз, чтобы потом они отображались без проблем                  
       tmpOutlines[i] = O(Size())
    end
    return unpack(tmpOutlines)
end
if index == 2 then
    for i=1,lines do                  
        SetValue(1,i,nil)
    end
    return nil
end
 
Здравствуйте,
Приведите полную версию кода.
 
Цитата
Sergey Gorokhov написал:
Здравствуйте,
Приведите полную версию кода.
Здравствуйте.

Код простой.
Код
require("StaticVar")

Settings ={
    Name = "*priceProfile",
    shift = 150,
    ChartId = "Sheet11"
}   

lines = 150
min_price_step = 1
scale = 2

function Init()
    Settings.line = {}
    for i = 1, lines do
        Settings.line[i] = {}
        Settings.line[i] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
    end
        
    algoF = getResults()
    return lines

end

function getResults()
    
    local outlines = {}
    local priceProfile = {}
    local tmpOutlines = {}

    return function(index, Fsettings)

        local shift = Fsettings.shift or 150
        local bars = 50

      if index == 1 then
            outlines = {}
            tmpOutlines = {}
            for i=1,lines do --необходимо вывести все линии хотя бы один раз, чтобы потом они отображались без проблем                  
                tmpOutlines[i] = O(Size())
            end
            return unpack(tmpOutlines)
        end
        if index == 2 then
            for i=1,lines do                  
                SetValue(1,i,nil)
            end
            return nil
        end

        if index == Size() then
                    
            stv.UseNameSpace(Fsettings.ChartId)
            algoResults = stv.GetVar('priceProfile')
            
            priceProfile = {}

            if algoResults ~= nil and type(algoResults) == "table" then 
                
                for i=1,#outlines do                   
                
                    SetValue(index-shift-1,          i, nil)
                    SetValue(index-shift,            i, nil)
                    SetValue(outlines[i].index,   i, nil)
                    
                    outlines[i].index = index-shift
                    outlines[i].val = nil
                end            

                local MAXV = 0
                
                local maxCount = 0 
                for i, profileItem in pairs(algoResults) do
                    MAXV=math.max(MAXV,profileItem.vol)
                    maxCount = maxCount + 1
                    priceProfile[maxCount] = {price = profileItem.price, vol = profileItem.vol}
                end
                
                table.sort(priceProfile, function(a,b) return (a['vol'] or 0) > (b['vol'] or 0) end)

                for i=1,math.min(#priceProfile, lines) do                                        
                    if priceProfile[i]~=nil then
                        
                        outlines[i] = {index = index-shift, val = nil}
                        priceProfile[i].vol=math.floor(priceProfile[i].vol/MAXV*bars)
                       
                        if priceProfile[i].vol>0 then
                            outlines[i].index = index-shift+priceProfile[i].vol
                            outlines[i].val = priceProfile[i].price                           
                        end                       
                        
                        SetValue(index-shift,       i, outlines[i].val)
                        SetValue(outlines[i].index, i, outlines[i].val)
                    end                   
                end                
            end
        end

        return nil
    end
end
 
Нет, не помогает и такое решение.

Если было выведено при инициализации 100 линий (т.е. когда происходит первый вызов OnCalculate), то больше не показывает индикатор.
Далее линии вроде имеют значение, но не видны.
 
Приходится выводить всегда столько линий, сколько определено, накладывая одну на другую, чтобы они слились в одну для пользователя.
По мере поступления новых линий они будут тогда уже распределятся по уровням цены.
 
Цитата
Nikolay написал:
Код простой.
Это не полная версия кода.
В результате многое не понятно.

Цитата
Nikolay написал:
Если было выведено при инициализации 100 линий (т.е. когда происходит первый вызов OnCalculate), то больше не показывает индикатор.
Да так и было задумано.

Цитата
Nikolay написал:
Приходится выводить всегда столько линий, сколько определено, накладывая одну на другую, чтобы они слились в одну для пользователя.
Вы можете вывести меньше линий, остальные будут существовать но не отобразятся пока Вы их не выведите.
Выводить больше линий чем в Init() нельзя.
 
Инициализация производится на полные 150 линий

вот же
Код
lines = 150
min_price_step = 1
scale = 2

function Init()
    Settings.line = {}
    for i = 1, lines do
        Settings.line[i] = {}
        Settings.line[i] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
    end
        
    algoF = getResults()
    return lines
end

Проблема именно в том, что если выводить линии не все сразу, они почему-то потом не показываются, а только те, что имели значение при запуске.
Далее, по мере поступления новых данных (цена же двигается и торовый диапазон расширяется) в таблицу algoResults, надо выводить новые линии, но они не показываются. Именно визуально не видны, но при этом, что очень странно, GetValue возвращает значение.

Как написал выше, пришлось выводить ненулевые значения для каждой линии, накладывая одну на другую. Так работает.
 
Nikolay,
В том то и дело, что Вы привели не полную версию кода, в связи с чем о многом приходится догадываться.
Выводить значения всех линий вовсе не обязательно. ниже пример который явно это показывает:
Скрытый текст


результат:
 
Я привел весь код индикатора.
Больше кода в нем нет.

Вы хотите увидеть ка формируется табоица algoResults? Но она получается из dll. Это простая таблица в которой накопленный объем по цене.
Далее они просто выводятся.

В Вашем примере, данные не получаются онлайн. Вы их вывели по данным прошлых свечек. А значит, при первом вызове OnCalculate, все данные для линий получены. При установке индикатора OnCalculate вызывается же два раза. Хотя зачем?

Делаю предположение, что это как-то связано.

Вот, для примера, испорченный индикатор горизонтальных объемов. Он реализует такой же алгоритм, получая данные из свечек, а не из dll.
В таком варианте он не работает как надо:
Код
--logfile=io.open(getWorkingFolder().."\\LuaIndicators\\priceAvgProfile.txt", "w")

Settings={}
Settings.period = 150
Settings.shift = 100
Settings.Name = "*priceAvgProfile"
Settings.weeks = 0 -- 1 - текущая, отрицательное число - сколько прошлых недель, включая текущую
Settings.fixShift = 1 -- 1 - всегда смещено на указанное количество shift, если 0, то будет смещено на дату начала неделеи расчета
Settings.showMaxLine = 1
---------------------------------------------------------------------------------------

lines = 100
scale = 2
min_price_step = 1

function Init()
    Settings.line = {}
    Settings.line[1] = {}
    Settings.line[1] = {Name = 'maxVol', Color = RGB(255, 128, 64), Type = TYPE_LINE, Width = 2}
    for i = 1, lines do
        Settings.line[i+1] = {}
        Settings.line[i+1] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
    end
    
    myFFF = FFF()
    return lines
end

function OnCalculate(index)
    if index == 1 then
        DSInfo = getDataSourceInfo()        
        min_price_step = getParamEx(DSInfo.class_code, DSInfo.sec_code, "SEC_PRICE_STEP").param_value
        scale = getSecurityInfo(DSInfo.class_code, DSInfo.sec_code).scale
    end 
    return myFFF(index, Settings)
end

---------------------------------------------------------------------------------------
function FFF()

    local cacheL={}
    local cacheH={}
    local cacheC={}
    local weeksBegin={}
    local maxPriceLine={}

    local outlines = {}
    local calculated_buffer={}
    
    return function(ind, Fsettings)

        local period = Fsettings.period or 150
        local shift = Fsettings.shift or 150
        local weeks = Fsettings.weeks or 0
        local fixShift = Fsettings.fixShift or 0
        local showMaxLine = Fsettings.showMaxLine or 0
        local bars = 50

        shift = math.max(bars+1, shift)

        local index = ind

        if index == 1 then          
            maxPriceLine = {}
            weeksBegin = {}
            cacheL = {}
            cacheL[index] = 0           
            cacheH = {}
            cacheH[index] = 0           
            cacheC = {}
            cacheC[index] = 0           

            calculated_buffer = {}
            outlines = {}

            return nil
        end
    ------------------------------      

        --maxPriceLine[index] = maxPriceLine[index-1] 
        cacheL[index] = cacheL[index-1] 
        cacheH[index] = cacheH[index-1] 
        cacheC[index] = cacheC[index-1] 

        if not CandleExist(index) then
            return maxPriceLine[index]
        end

        cacheH[index] = H(index)
        cacheL[index] = L(index)
        cacheC[index] = C(index)
        
        if T(index).week_day<T(index-1).week_day or T(index).year>T(index-1).year then
            weeksBegin[#weeksBegin+1] = index
        end

        if index < Size() then return nil end   

        if calculated_buffer[index] ~= nil then
            return maxPriceLine[index]
        end

        if showMaxLine==1 then
            SetValue(index-shift-1, 1, nil)
            SetValue(index-shift,   1, nil)
        end

        for i=1,#outlines do                        
            SetValue(index-shift-1,          i+1, nil)
            SetValue(index-shift,            i+1, nil)
            SetValue(outlines[i].index,      i+1, nil)
            
            outlines[i].index = index-shift
            outlines[i].val = nil
        end            
        
        local beginIndex = index-period
        if weeks == 1 then
            beginIndex = weeksBegin[#weeksBegin] or beginIndex
        end
        if weeks < 0 then
            beginIndex = weeksBegin[#weeksBegin+weeks] or beginIndex
        end
        
        if fixShift==0 then
            shift = math.max(bars+1, index-beginIndex)
        end

        --WriteLog('weeks '..tostring(weeks)..' last '..tostring(weeksBegin[#weeksBegin])..' beginIndex '..tostring(beginIndex))
        
        local maxPrice = math.max(unpack(cacheH,math.max(beginIndex, 1),index))
        local minPrice = math.min(unpack(cacheL,math.max(beginIndex, 1),index))       
        
        ----------------------------------------
        local priceProfile = {}
        --local clasterStep = math.max((maxPrice - minPrice)/lines, min_price_step)
        --local clasterStep = (maxPrice - minPrice)/lines
        local clasterStep = 10*min_price_step

        --WriteLog('minPrice '..tostring(minPrice)..' maxPrice '..tostring(maxPrice)..' clasterStep '..tostring(clasterStep))
        
        for i = 0, (index-beginIndex) do
            if CandleExist(index-i) then                
                local barSteps = math.max(math.ceil((H(index-i) - L(index-i))/clasterStep),1)
                for j=0,barSteps-1 do
                    local clasterPrice = math.floor((L(index-i) + j*clasterStep)/clasterStep)*clasterStep
                    local clasterIndex = clasterPrice*math.pow(10, scale)
                    if priceProfile[clasterIndex] == nil then
                        priceProfile[clasterIndex] = {price = clasterPrice, vol = 0}
                    end
                    priceProfile[clasterIndex].vol = priceProfile[clasterIndex].vol + V(index-i)/barSteps
                end
            end
        end

        --------------------
        local MAXV = 0
        local maxPrice = 0
        local maxCount = 0 

        local sortedProfile = {}

        for i, profileItem in pairs(priceProfile) do
            MAXV=math.max(MAXV,profileItem.vol)
            if MAXV == profileItem.vol then
                maxPrice=profileItem.price
            end
            maxCount = maxCount + 1
            sortedProfile[maxCount] = {price = profileItem.price, vol = profileItem.vol}
        end
                
        --WriteLog('maxV '..tostring(MAXV)..' tblMax '..tostring(sortedProfile[1].vol))

        if maxPrice == 0 then
            maxPrice = O(index) 
        end

        table.sort(sortedProfile, function(a,b) return (a['vol'] or 0) > (b['vol'] or 0) end)

        ---------------------
        for i=1,lines do                                        

            
            if sortedProfile[i]~=nil then               
                outlines[i] = {index = index-shift+bars, val = maxPrice}
                sortedProfile[i].vol=math.floor(sortedProfile[i].vol/MAXV*bars)
                if sortedProfile[i].vol>0 then
                    outlines[i].index = index-shift+sortedProfile[i].vol
                    outlines[i].val = sortedProfile[i].price                           
                end                                               
                SetValue(index-shift,       i+1, outlines[i].val)
                SetValue(outlines[i].index, i+1, outlines[i].val)
            end                   
                    
            --WriteLog('line '..tostring(i).." price "..tostring(GetValue(index-shift, i)).." - "..tostring(GetValue(outlines[i].index, i)).." vol "..tostring(outlines[i].index-index+shift))
                
        end                

        if showMaxLine==1 then
            SetValue(index-shift, 1, maxPrice)
            maxPriceLine[index] = maxPrice
        end

        calculated_buffer[index] = true
                
        return maxPriceLine[index]
    end
end

function WriteLog(text)

    logfile:write(tostring(os.date("%c",os.time())).." "..text.."\n");
    logfile:flush();
    LASTLOGSTRING = text;
 
end
 
А вот сделав такие изменения в цикл вывода строк, все работает.
Код
        for i=1,lines do                                        

            outlines[i] = {index = index-shift+bars, val = maxPrice}
            
            if sortedProfile[i]~=nil then               
                sortedProfile[i].vol=math.floor(sortedProfile[i].vol/MAXV*bars)
                if sortedProfile[i].vol>0 then
                    outlines[i].index = index-shift+sortedProfile[i].vol
                    outlines[i].val = sortedProfile[i].price                           
                end                                               
            end                   
            SetValue(index-shift,       i+1, outlines[i].val)
            SetValue(outlines[i].index, i+1, outlines[i].val)
                    
            --WriteLog('line '..tostring(i).." price "..tostring(GetValue(index-shift, i)).." - "..tostring(GetValue(outlines[i].index, i)).." vol "..tostring(outlines[i].index-index+shift))
                
        end

Код
--logfile=io.open(getWorkingFolder().."\\LuaIndicators\\priceAvgProfile.txt", "w")

Settings={}
Settings.period = 150
Settings.shift = 100
Settings.Name = "*priceAvgProfile"
Settings.weeks = 0 -- 1 - текущая, отрицательное число - сколько прошлых недель, включая текущую
Settings.fixShift = 1 -- 1 - всегда смещено на указанное количество shift, если 0, то будет смещено на дату начала неделеи расчета
Settings.showMaxLine = 1
---------------------------------------------------------------------------------------

lines = 100
scale = 2
min_price_step = 1

function Init()
    Settings.line = {}
    Settings.line[1] = {}
    Settings.line[1] = {Name = 'maxVol', Color = RGB(255, 128, 64), Type = TYPE_LINE, Width = 2}
    for i = 1, lines do
        Settings.line[i+1] = {}
        Settings.line[i+1] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
    end
    
    myFFF = FFF()
    return lines
end

function OnCalculate(index)
    if index == 1 then
        DSInfo = getDataSourceInfo()        
        min_price_step = getParamEx(DSInfo.class_code, DSInfo.sec_code, "SEC_PRICE_STEP").param_value
        scale = getSecurityInfo(DSInfo.class_code, DSInfo.sec_code).scale
    end 
    return myFFF(index, Settings)
end

---------------------------------------------------------------------------------------
function FFF()

    local cacheL={}
    local cacheH={}
    local cacheC={}
    local weeksBegin={}
    local maxPriceLine={}

    local outlines = {}
    local calculated_buffer={}
    
    return function(ind, Fsettings)

        local period = Fsettings.period or 150
        local shift = Fsettings.shift or 150
        local weeks = Fsettings.weeks or 0
        local fixShift = Fsettings.fixShift or 0
        local showMaxLine = Fsettings.showMaxLine or 0
        local bars = 50

        shift = math.max(bars+1, shift)

        local index = ind

        if index == 1 then          
            maxPriceLine = {}
            weeksBegin = {}
            cacheL = {}
            cacheL[index] = 0           
            cacheH = {}
            cacheH[index] = 0           
            cacheC = {}
            cacheC[index] = 0           

            calculated_buffer = {}
            outlines = {}

            return nil
        end
    ------------------------------      

        --maxPriceLine[index] = maxPriceLine[index-1] 
        cacheL[index] = cacheL[index-1] 
        cacheH[index] = cacheH[index-1] 
        cacheC[index] = cacheC[index-1] 

        if not CandleExist(index) then
            return maxPriceLine[index]
        end

        cacheH[index] = H(index)
        cacheL[index] = L(index)
        cacheC[index] = C(index)
        
        if T(index).week_day<T(index-1).week_day or T(index).year>T(index-1).year then
            weeksBegin[#weeksBegin+1] = index
        end

        if index < Size() then return nil end   

        if calculated_buffer[index] ~= nil then
            return maxPriceLine[index]
        end

        if showMaxLine==1 then
            SetValue(index-shift-1, 1, nil)
            SetValue(index-shift,   1, nil)
        end

        for i=1,#outlines do                        
            SetValue(index-shift-1,          i+1, nil)
            SetValue(index-shift,            i+1, nil)
            SetValue(outlines[i].index,      i+1, nil)
            
            outlines[i].index = index-shift
            outlines[i].val = nil
        end            
        
        local beginIndex = index-period
        if weeks == 1 then
            beginIndex = weeksBegin[#weeksBegin] or beginIndex
        end
        if weeks < 0 then
            beginIndex = weeksBegin[#weeksBegin+weeks] or beginIndex
        end
        
        if fixShift==0 then
            shift = math.max(bars+1, index-beginIndex)
        end

        --WriteLog('weeks '..tostring(weeks)..' last '..tostring(weeksBegin[#weeksBegin])..' beginIndex '..tostring(beginIndex))
        
        local maxPrice = math.max(unpack(cacheH,math.max(beginIndex, 1),index))
        local minPrice = math.min(unpack(cacheL,math.max(beginIndex, 1),index))       
        
        ----------------------------------------
        local priceProfile = {}
        --local clasterStep = math.max((maxPrice - minPrice)/lines, min_price_step)
        --local clasterStep = (maxPrice - minPrice)/lines
        local clasterStep = 10*min_price_step

        --WriteLog('minPrice '..tostring(minPrice)..' maxPrice '..tostring(maxPrice)..' clasterStep '..tostring(clasterStep))
        
        for i = 0, (index-beginIndex) do
            if CandleExist(index-i) then                
                local barSteps = math.max(math.ceil((H(index-i) - L(index-i))/clasterStep),1)
                for j=0,barSteps-1 do
                    local clasterPrice = math.floor((L(index-i) + j*clasterStep)/clasterStep)*clasterStep
                    local clasterIndex = clasterPrice*math.pow(10, scale)
                    if priceProfile[clasterIndex] == nil then
                        priceProfile[clasterIndex] = {price = clasterPrice, vol = 0}
                    end
                    priceProfile[clasterIndex].vol = priceProfile[clasterIndex].vol + V(index-i)/barSteps
                end
            end
        end

        --------------------
        local MAXV = 0
        local maxPrice = 0
        local maxCount = 0 

        local sortedProfile = {}

        for i, profileItem in pairs(priceProfile) do
            MAXV=math.max(MAXV,profileItem.vol)
            if MAXV == profileItem.vol then
                maxPrice=profileItem.price
            end
            maxCount = maxCount + 1
            sortedProfile[maxCount] = {price = profileItem.price, vol = profileItem.vol}
        end
                
        --WriteLog('maxV '..tostring(MAXV)..' tblMax '..tostring(sortedProfile[1].vol))

        if maxPrice == 0 then
            maxPrice = O(index) 
        end

        table.sort(sortedProfile, function(a,b) return (a['vol'] or 0) > (b['vol'] or 0) end)

        ---------------------
        for i=1,lines do                                        

            outlines[i] = {index = index-shift+bars, val = maxPrice}
            
            if sortedProfile[i]~=nil then               
                sortedProfile[i].vol=math.floor(sortedProfile[i].vol/MAXV*bars)
                if sortedProfile[i].vol>0 then
                    outlines[i].index = index-shift+sortedProfile[i].vol
                    outlines[i].val = sortedProfile[i].price                           
                end                                               
            end                   
            SetValue(index-shift,       i+1, outlines[i].val)
            SetValue(outlines[i].index, i+1, outlines[i].val)
                    
            --WriteLog('line '..tostring(i).." price "..tostring(GetValue(index-shift, i)).." - "..tostring(GetValue(outlines[i].index, i)).." vol "..tostring(outlines[i].index-index+shift))
                
        end                

        if showMaxLine==1 then
            SetValue(index-shift, 1, maxPrice)
            maxPriceLine[index] = maxPrice
        end

        calculated_buffer[index] = true
                
        return maxPriceLine[index]
    end
end

function WriteLog(text)

    logfile:write(tostring(os.date("%c",os.time())).." "..text.."\n");
    logfile:flush();
    LASTLOGSTRING = text;
 
end
 
маска-пленка с лифтинг-эффектом
Новейшая разработка - маска с коллоидным золотом для безоперационной подтяжки кожи лица и шеи.
Реанимирует выработку коллагена, разглаживает морщины и подтягивает кожу, а также чистит поры и удаляет черные точки. 100% натуральный состав.

Инновационная маска с коллоидным золотом для безоперационной подтяжки кожи лица и шеи
 
Nikolay,
Информация получена, проблема изучается. Постараемся в ближайшее время дать ответ.
 
Добрый день.

Ошибка, описанная в данном инциденте, связано с некорректной работой ПО терминала QUIK и будет исправлена в одной из очередных версий программы.
Приносим извинения за причиненные неудобства.
 
Nikolay, Добрый день,
     
      Описанная в данном инциденте проблема была устранена в версии       7.23.0 терминала QUIK.
      Данная версия была разослана всем брокерам системы QUIK в рамках       стандартной процедуры обновления версии 23.11.2018.
      По поводу получения обновления рекомендуем вам обратиться к своему       брокеру.
     
      Приносим извинения за причиненные неудобства.
Страницы: 1
Читают тему (гостей: 1)
Наверх