Индикатор с большим числом линий.

Страницы: 1
RSS
Индикатор с большим числом линий.
 
В последних версиях терминала если добавить индикатор где число линий, например, больше 50, то терминал просто "умирает". Этот же индикатор в 7-ой версии вполне себе работал, даже не на одном графике.

Вот простейший пример, ничего, по сути, не делающий, а просто выводит линии на график, демонстрирующий проблему.
Код


local lines = 100

Settings = {}
Settings.Name = "*test_lines"
Settings.price = 66960
Settings.delta = 1.0

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

function OnChangeSettings()
    Init()
end

function OnCalculate(index)
    if index < Size() then return end
    for i = 1, lines do
        SetRangeValue(i, index-100, index-1, Settings.price-i*Settings.delta)
    end
    return
end
 
Пример не совсем тот.
В примере  Вы выводите значения с функциями Lua.
Это н совсем то, когда вывод делается с помощью return ... значения индикаторов.
-----------------------  
Для чистоты эксперимента сделайте вывод значений через return.
-------------------------
У меня 42 индикатора.
Отображаются практически мгновенно.
Версия 8.7.1.3
 
Пример именно такой, какой нужен, т.к. я по большей части вывожу данные назад по барам, т.к. алгоритм не предполагает простого линейного расчёта.
Иначе можно говорить, что теперь у методов установки данных на график есть ограничения. Или проблема, что более вероятно, т.к. при добавлении оного на график потребление ресурсов процессора повышается с 0% до 9-10%.
Что тоже не много, но терминал уже не отвечает.
 
Цитата
Nikolay написал:
 SetRangeValue(i, index-100, index-1, Settings.price-i*Settings.delta)
В примере каждый раз выводится 100 значений 100 линий. Это 10 000 вызовов функции SetRangeValue, в которой есть вычисления на луа.
OnCalculate(index)  вызывается на каждый тик.
В результате у Вас цикл не успевает завершится до получения нового тика.
--------------------------
Попробуйте измерить время вывода одного значения, чтобы понять где тормозит.
 
Зачем строить индикатор назад на каждый тик?
 
Поясняю. Вы стрите индикатор на 99 значений закрытых свечей плюс один тик текущей свечи.
Какой в этом смысл.
Полагаю, что у вас индикатор не изменяется назад на 99 свечей на каждом тике текущей свечи.
 
Цитата
nikolz написал:
Зачем строить индикатор назад на каждый тик?
Это для примера. Реальный строится только каждый бар. на демо счёте по Сбербанку, где сделка раз минуту - этот пример вполне показательный. Впрочем, добавить кеш уже обработанных индексов - не проблема, проблема в другом.
 
Не берусь судить о том, что в очередной раз "изобрели и наваяли" Разработчики, хочу лишь добавить, что в версиях 12.*.*.* при входе в терминал по долгу читаются самописные луа индикаторы (значительно по долгу).
Хотя допускаю, что это и моих рук дело, может быть?

Тема написания индикаторов на луа многократно обсуждалась и разбиралась на форуме, и тем не менее, нет внутренней уверенности, что реализация луа индикаторов как минимум оптимальна? Вот и я в очередной раз столкнулся с проблемой создания надежной реализации индикаторов при реализации подхода по Wilder (реализация индикаторов и стратегий). Собственно трудности вызывает не сам алгоритм расчетов, а особенности реализации скриптов луа в QUIK.

Задачу ставил следующую:
1) Скрипт должен использоваться без переделок, как в OnCalculate, так и в потоке луа;
2) Одинаково быстро работать на разных тайм фреймах;
3) Технологическое исполнение (правила написания) должны быть едины, выполняться принцип создания "по Образу и Подобию".
Код через класс луа, требует доработки, и вызывает у меня некоторые сложности в реализации, хотя интуитивно на мой взгляд и более предпочтителен, так как хранит свое внутреннее состояние (инкапсулирует). Вот что получилось при использовании подхода с замыканием, код привожу ниже, там же см. в комментариях и порядок написания, единая технология написания скрипта, на мой взгляд логична?
Код
function Wilder.RSI_Indicator()
    -- 1. Локальные утилиты для оптимизации
    local math_abs = math.abs
    local math_max = math.max
    local math_min = math.min
    local math_floor = math.floor
    local string_format = string.format
    
    -- Форматирование даты и времени
    local get_date = function(td)
        return td and string_format("%.4d%.2d%.2d", td.year, td.month, td.day) or "00000000"
    end
    local get_time = function(td)
        return td and string_format("%.2d%.2d%.2d", td.hour, td.min, td.sec) or "000000"
    end
    
    -- Функция округления
    local round = function(x, n)
        n = n or 0
        local mult = 10 ^ n
        return x >= 0 and math_floor(x * mult + 0.5) / mult or math_floor(x * mult - 0.5) / mult
    end

    -- 2. Функции сглаживания
    local smoothing = {
        EMA = function(new_val, prev, period)
            local alpha = 2 / (period + 1)
            return alpha * new_val + (1 - alpha) * prev
        end,

        SMMA = function(new_val, prev, period, bars_count)
            if bars_count <= period then
                return (prev * (bars_count - 1) + new_val) / bars_count
            else
                return (prev * (period - 1) + new_val) / period
            end
        end
    }

    -- 3. Состояние индикатора
    local cache = {
        initialized = false,
        last_index = 0,
        history = {},  -- Хранилище для исторических значений
        Configs = {
            period = 14,
            method = "SMMA",
            max_history = 100
        },
        prev_price = nil
    }

    return function(I, F, ds)
        -- A. Обновление параметров
        if F then
            cache.Configs.period = F.Period or cache.Configs.period
            cache.Configs.method = F.Method or cache.Configs.method
            cache.Configs.max_history = F.Max_History or cache.Configs.max_history
        end

        local period = cache.Configs.period
        local method = cache.Configs.method

        -- B. Проверка нового бара
        local is_new_bar = I > cache.last_index

        -- C. Очистка старых данных при новом баре
        if is_new_bar then
            cache.last_index = I
            
            -- Удаляем устаревшие данные
            local min_index = I - cache.Configs.max_history
            for i = min_index - 10, min_index do
                if cache.history[i] then
                    cache.history[i] = nil
                end
            end
        end

        -- D. Получение текущей цены закрытия
        local current_price = Value(I, "C", ds)
        if not current_price then
            return {
                d = "00000000",
                t = "000000",
                rsi = 50,
                rsi1 = 50,
                rsi2 = 50,
                rsi3 = 50,
                gain = 0,
                loss = 0,
                avg_gain = 0,
                avg_loss = 0
            }
        end

        -- E. Инициализация записи для текущего индекса
        if not cache.history[I] then
            cache.history[I] = {
                price = current_price,
                change = 0,
                gain = 0,
                loss = 0,
                avg_gain = 0,
                avg_loss = 0,
                rsi = 50
            }
        end

        -- F. Расчет изменения цены
        local prev_price = cache.prev_price
        if not prev_price then
            -- Для первого бара используем текущую цену как предыдущую
            prev_price = current_price
        end

        local change = current_price - prev_price
        cache.history[I].change = change
        cache.prev_price = current_price  -- Сохраняем для следующего вызова

        -- G. Расчет положительных и отрицательных изменений
        local gain = math_max(change, 0)
        local loss = math_max(-change, 0)

        cache.history[I].gain = gain
        cache.history[I].loss = loss

        -- H. Расчет средних значений
        if I == 1 then
            -- Первый бар
            cache.history[I].avg_gain = gain
            cache.history[I].avg_loss = loss
        else
            local prev_entry = cache.history[I-1] or cache.history[I]
            local bars_count = math_min(I, period)
            
            if method == "SMMA" then
                cache.history[I].avg_gain = smoothing.SMMA(
                    gain, 
                    prev_entry.avg_gain, 
                    period, 
                    bars_count
                )
                
                cache.history[I].avg_loss = smoothing.SMMA(
                    loss, 
                    prev_entry.avg_loss, 
                    period, 
                    bars_count
                )
            else -- EMA
                cache.history[I].avg_gain = smoothing.EMA(
                    gain, 
                    prev_entry.avg_gain, 
                    period
                )
                
                cache.history[I].avg_loss = smoothing.EMA(
                    loss, 
                    prev_entry.avg_loss, 
                    period
                )
            end
        end

        -- I. Расчет RSI
        local avg_gain = cache.history[I].avg_gain
        local avg_loss = cache.history[I].avg_loss
        
        local rs = (avg_loss > 1e-5) and (avg_gain / avg_loss) or 0
        local rsi = 100 - (100 / (1 + rs))
        
        -- Корректировка граничных значений
        rsi = math_max(0, math_min(100, rsi))
        cache.history[I].rsi = rsi

        -- J. Получение предыдущих значений
        local prev1 = cache.history[I-1] or {rsi = 50}
        local prev2 = cache.history[I-2] or {rsi = 50}
        local prev3 = cache.history[I-3] or {rsi = 50}
        
        -- K. Формирование результата
        local td = ds:T(I)
        local Out = {
            d = get_date(td),
            t = get_time(td),
            rsi = round(rsi, 2),
            rsi1 = round(prev1.rsi, 2),
            rsi2 = round(prev2.rsi, 2),
            rsi3 = round(prev3.rsi, 2),
            gain = round(gain, 4),
            loss = round(loss, 4),
            avg_gain = round(avg_gain, 4),
            avg_loss = round(avg_loss, 4),
            _cache = cache.history[I]
        }
        Wilder.Log( tostring(I)..') '.. Out.d ..' / '.. Out.t 
        ..'; rsi = ' .. tostring(Out.rsi) 
        ..'; rsi1 = ' .. tostring(Out.rsi1) 
        ..'; rsi2 = ' .. tostring(Out.rsi2) 
        ..'; rsi3 = ' .. tostring(Out.rsi3) 

        ..'; gain = ' .. tostring(Out.gain) 
        ..'; loss = ' .. tostring(Out.loss)
        
        --..'; atr = ' .. tostring(Out.atr) 
        ..'; avg_gain = ' .. tostring(Out.avg_gain) 
        ..'; avg_loss = ' .. tostring(Out.avg_loss) 

        --..'; trend = ' .. tostring(Out.trend)
        --..'; trend1 = ' .. tostring(Out.trend1)
        --..'; trend2 = ' .. tostring(Out.trend2)

        --..'; signal = ' .. tostring(Out.signal)
        --..'; signal1 = ' .. tostring(Out.signal1)
        --..'; signal2 = ' .. tostring(Out.signal2)
        )--[[--]]
        cache.initialized = true

        return Out
    end
end
 
Все же эта тема посвящена конкретной технической проблеме терминала.
 
Цитата
Nikolay написал:

local lines = 100

Settings = {}
Settings.Name = "*test_lines"
Settings.price = 66960
Settings.delta = 1.0

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

function OnChangeSettings()
   Init()
end

function OnCalculate(index)
   if index < Size() then return end
   for i = 1, lines do
       SetRangeValue(i, index-100, index-1, Settings.price-i*Settings.delta)
   end
   return
end
попробую Ваш пример потестить.
 
Цитата
Nikolay написал:
Все же эта тема посвящена конкретной технической проблеме терминала.
На демо сервере  версия 12.4.0.38  Ваш тест работает мгновенно.
 
интервал любой .
 
Цитата
Nikolay написал:
Все же эта тема посвящена конкретной технической проблеме терминала.
Nikolay,  Весь этот форум посвящён одной большой проблеме, если Вы думаете что устранив данную, не будет похожей,  через некоторое время, я думаю мягко говоря Вы ошибаетесь.Думаю нужна профессиональная смекалка, или как тут выражался пользователь "покумекать", чтоб по крайней мере следующая проходила мимо.

Код который я выложил несет в себе две основные особенности. 1) Буферизация. (Зачем скажем 65 000 свечей если окно 14 бар?); 2) Своевременную очистку данных (Зачем скажем 65 000 свечей хранить, если окно 14 бар); ну и конечно момент обновлений и расчётов, зачем рассчитывать каждый тик, если квик с трудом обновляет данные за одну секунду?
 
Цитата
Nikolay,  Весь этот форум посвящён одной большой проблеме, если Вы думаете что устранив данную, не будет похожей,  через некоторое время, я думаю мягко говоря Вы ошибаетесь.Думаю нужна профессиональная смекалка, или как тут выражался пользователь "покумекать", чтоб по крайней мере следующая проходила мимо.

Код который я выложил несет в себе две основные особенности. 1) Буферизация. (Зачем скажем 65 000 свечей если окно 14 бар?); 2) Своевременную очистку данных (Зачем скажем 65 000 свечей хранить, если окно 14 бар); ну и конечно момент обновлений и расчётов, зачем рассчитывать каждый тик, если квик с трудом обновляет данные за одну секунду?
Я уже говорил Вам, что я прекрасно знаю как и что делать для оптимизации кода индикатора. И рабочий код не рассчитывает ничего каждый тик, не хранит историю на все бары, т.к. это бессмысленно и т.д. Здесь предоставлен пример, воспроизводящий проблему. Эта тема посвящена только одному - большое число линий на графике. Все. Хотя, если подумать, странно ожидать, что терминал не может справится с такой простой нагрузкой даже каждый тик.

Впрочем, Вам явно необходимо высказаться. Вот пример без расчёта на каждый тик. И он точно также "убивает" терминал. У Вас работает, у меня нет. Поэтому мне интересно мнение разработчиков.

Код
local lines = 100
Settings = {}
Settings.Name = "*test_lines"
Settings.price = 313
Settings.delta = 0.01
local cache = {}
function Init()
    Settings.line = {}
    for i = 1, lines do
        Settings.line[i] = {}
        Settings.line[i] = {Color = RGB(185, 185, 185), Type = TYPET_BAR, Width = 2}
    end
    cache = {}
    return lines
end

function OnChangeSettings()
    Init()
end
function OnCalculate(index)
    if index < Size() then return end
    if cache[index] then return end
    for i = 1, lines do
        SetRangeValue(i, index-100, index-1, Settings.price-i*Settings.delta)
    end
    cache[index] = true
    message('calc '..tostring(index))
    return
end
 
Nikolay, версия с cache на SBER, 15 ничего не показывает у меня при любом delta (хотя SetRangeValue возвращает true), и не вешает терминал. Я тестил без подключения к серверу.
Страницы: 1
Читают тему
Наверх