Вызов getDataSourceInfo() из Init() в Lua индикаторах

Страницы: 1
RSS
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Добрый день!

При написании своего специфического индикатора иногда возникает необходимость делать некоторые начальные настройки в его работе в зависимости от того, для какого инструмента он строится.
Однако, из Init() вызывать getDataSourceInfo() бессмысленно (мы получим информацию только о тайм-фрейме). Перезапускать рабочее место Quik после добавления индикатора на график, как указано в "Руководстве пользователя QLua" как-то не хочется.
(На самом, деле нужно как-то заставить Quik повторно вызвать Init() индикатора, что можно, впрочем, сделать и без перезапуска рабочего места, а просто некоторыми манипуляциями с самим окном графика. Но это тоже не выход).

Что посоветуете?

Первое, что мне самому приходит в голову, это выполнить нужные настройки при первом же вызове OnCalculate().
getDataSourceInfo() здесь уже будет работать корректно, т.к. к этому моменту Quik уже "привязал" новый индикатор к источнику данных.
Но это тоже не самый красивый ход: каждый раз проверять индекс, отлавливая именно первую свечку, чтобы не заниматься настройками вообще при каждом вызове OnCalculate() и т.д.

Вопрос к разработчикам: я так понимаю, что, при добавлении Lua индикатора на график, Init() вызывается до "подключения" к источнику данных. А что мешает его вызвать после? Так ли важно знать количество линий на индикаторе до "подключения" к источнику данных? Если обеспечить корректную работу getDataSourceInfo() при первом после добавления индикатора на график вызове Init() все же нельзя, то хотел бы попросить разработчиков добавить дополнительный СallBack, типа Init2() после "подключения" к источнику данных, но до первого вызова OnCalculate(). Можно, например, это делать не для каждого индикатора, а только если Init() вернет "соответствующую просьбу".
 
Здравствуйте,
Задача вполне решается проверкой первой свечи в OnCalculate
Из приведенного описания не вполне понятно, чем данный подход не устраивает.
 
Здравствуйте,
Это то понятно, что решить задачу можно, отлавливая вызов для первой свечки, но это лишние накладные расходы, пускай и мизерные. А на медленных машинках, для процессов, исполняемых интерпретатором, если кто-то решит повесить много индикаторов, в каждом из которых по несколько линий ...:), уже можно заметить подтормаживания.

Но не в этом суть.
Не прослеживается единообразия в логике вызовов Init() со стороны Quik: при исходном добавлении индикатора на график, Init() вызывается до привязки к источнику данных, а при замене инструмента Init(), почему-то, вызывается уже после привязки к новому инструменту.
 
Цитата
Алексей написал:
Не прослеживается единообразия в логике вызовов Init() со стороны Quik: при исходном добавлении индикатора на график, Init() вызывается до привязки к источнику данных, а при замене инструмента Init(), почему-то, вызывается уже после привязки к новому инструменту.
При добавлении индикатора сразу указывается Источник данных. Поэтому описанное поведение больше похоже на ошибку в логике.
Надо делать так, как надо. А как не надо - делать не надо.
 
Цитата
Старатель написал:
Цитата
Алексей   написал:
Не прослеживается единообразия в логике вызовов Init() со стороны Quik: при исходном добавлении индикатора на график, Init() вызывается до привязки к источнику данных, а при замене инструмента Init(), почему-то, вызывается уже после привязки к новому инструменту.
При добавлении индикатора сразу указывается Источник данных. Поэтому описанное поведение больше похоже на ошибку в логике.
В том-то и дело, что при вызове getDataSourceInfo() из Init() Вы не получите информацию об инструменте, на основе свечек которого должен строиться индикатор. На это даже сами разработчики Quik указывают в  "Руководстве пользователя QLua". Имеется в виду самый первый вызов Init() непосредственно после добавления индикатора в окно графика.

To: Старатель.
Под "привязкой" я имею в виду не действия пользователя, естественно указавшего для какого инструмента он желает видеть индикатор, а инициализацию внутренних служебных таблиц о самом инструменте, а также о ds (DataSource) с информацией о свечках, о методах H L O C V Size тд., которую Quik в вышеописанном случае производит уже только после вызова Init().
 
Цитата
Алексей написал:
Это то понятно, что решить задачу можно, отлавливая вызов для первой свечки, но это лишние накладные расходы, пускай и мизерные. А на медленных машинках, для процессов, исполняемых интерпретатором, если кто-то решит повесить много индикаторов, в каждом из которых по несколько линий ...:),

1) То что проверка первой свечи приводит к "накладным расходам" не более чем просто слова.
2) Гипотетическое создание Init2 совершенно никак не позволит изменить ситуацию. И даже если ее добавить, это будет ровно тоже самое что проверка первой свечи.
3) Решение в виде проверки первой свечи, в полной мере решает задачу и аргументов которые не позволят ее решить указанным способом, Вы так и не привели.
 
Цитата
Sergey Gorokhov написал:
Цитата
Алексей   написал:
Это то понятно, что решить задачу можно, отлавливая вызов для первой свечки, но это лишние накладные расходы, пускай и мизерные. А на медленных машинках, для процессов, исполняемых интерпретатором, если кто-то решит повесить много индикаторов, в каждом из которых по несколько линий ...:),
1) То что проверка первой свечи приводит к "накладным расходам" не более чем просто слова.
2) Гипотетическое создание Init2 совершенно никак не позволит изменить ситуацию. И даже если ее добавить, это будет ровно тоже самое что проверка первой свечи.
3) Решение в виде проверки первой свечи, в полной мере решает задачу и аргументов которые не позволят ее решить указанным способом, Вы так и не привели.
отлавливание первой свечи оператором
if indx==1 then ... end
на медленных машинах займет не более  50 мкс.
А поступление очередных данных происходит не чаще, чем раз в  100 мс.
Разница примерно в 2000 раз.
А задержка реакции OC не менее 10 мс.
Разница в 200 раз.
А задержка отправки коротких сообщений с компа на сервер брокера типа заявок, может составить 200 мс
Разница примерно в 4000 раз.
За что боремся?
 
Цитата
Николай Камынин написал:
Цитата
Sergey Gorokhov   написал:
Цитата
Алексей   написал:
Это то понятно, что решить задачу можно, отлавливая вызов для первой свечки, но это лишние накладные расходы, пускай и мизерные. А на медленных машинках, для процессов, исполняемых интерпретатором, если кто-то решит повесить много индикаторов, в каждом из которых по несколько линий ...:),
1) То что проверка первой свечи приводит к "накладным расходам" не более чем просто слова.
2) Гипотетическое создание Init2 совершенно никак не позволит изменить ситуацию. И даже если ее добавить, это будет ровно тоже самое что проверка первой свечи.
3) Решение в виде проверки первой свечи, в полной мере решает задачу и аргументов которые не позволят ее решить указанным способом, Вы так и не привели.
отлавливание первой свечи оператором
if indx==1 then ... end
на медленных машинах займет не более  50 мкс.
А поступление очередных данных происходит не чаще, чем раз в  100 мс.
Разница примерно в 2000 раз.
А задержка реакции OC не менее 10 мс.
Разница в 200 раз.
А задержка отправки коротких сообщений с компа на сервер брокера типа заявок, может составить 200 мс
Разница примерно в 4000 раз.
За что боремся?
Я же нарисовал смайлик. И сразу далее написал, что естественно не в этом суть.
А суть вопроса к quik в том, что:

Не прослеживается единообразия в логике вызовов Init() со стороны Quik: при исходном добавлении индикатора на график, Init() вызывается до привязки к источнику данных, а при замене инструмента Init(), почему-то, вызывается уже после привязки к новому инструменту.
 
Цитата
Алексей написал:
Цитата
Николай  Камынин   написал:
Цитата
Sergey Gorokhov   написал:
Цитата
Алексей   написал:
Это то понятно, что решить задачу можно, отлавливая вызов для первой свечки, но это лишние накладные расходы, пускай и мизерные. А на медленных машинках, для процессов, исполняемых интерпретатором, если кто-то решит повесить много индикаторов, в каждом из которых по несколько линий ...  :)  ,
1) То что проверка первой свечи приводит к "накладным расходам" не более чем просто слова.
2) Гипотетическое создание Init2 совершенно никак не позволит изменить ситуацию. И даже если ее добавить, это будет ровно тоже самое что проверка первой свечи.
3) Решение в виде проверки первой свечи, в полной мере решает задачу и аргументов которые не позволят ее решить указанным способом, Вы так и не привели.
отлавливание первой свечи оператором
if indx==1 then ... end
на медленных машинах займет не более  50 мкс.
А поступление очередных данных происходит не чаще, чем раз в  100 мс.
Разница примерно в 2000 раз.
А задержка реакции OC не менее 10 мс.
Разница в 200 раз.
А задержка отправки коротких сообщений с компа на сервер брокера типа заявок, может составить 200 мс
Разница примерно в 4000 раз.
За что боремся?
Я же нарисовал смайлик. И сразу далее написал, что естественно не в этом суть.
А суть вопроса к quik в том, что:

Не прослеживается единообразия в логике вызовов Init() со стороны Quik: при исходном добавлении индикатора на график, Init() вызывается до привязки к источнику данных, а при замене инструмента Init(), почему-то, вызывается уже после привязки к новому инструменту.
А в квике как мультике про простоквашено (Письмо дяди Федора).
Начинал писать квик один писатель потом второй и т д.
Стратегию построения КВИКА разработали еще в прошлом веке.
Вот и нет однообразия.
А читатели - это клиенты брокеров.
 
еще замечу, что в init ставить настройку каких либо параметров не имеет смысла, так как он не вызывается при изменении параметров индикаторов.
Поэтому без проверки на 1 индекса Вы все рано не обойдетесь.
И смысла делать как вы хотите нет никакого.
 
Цитата
Николай Камынин написал:
еще замечу, что в init ставить настройку каких либо параметров не имеет смысла, так как он не вызывается при изменении параметров индикаторов.
Поэтому без проверки на 1 индекса Вы все рано не обойдетесь.
И смысла делать как вы хотите нет никакого.
Init() вызывается при:
1. Первоначальном добавлении индикатора на график,
2. При каждой смене тайм-фрейма (смена набора свечек при сохранении инструмента),
3. При каждой смене инструмента (смена источника данных для свечек).
4. На старте quik и, что по сути для индикатора тоже самое, при загрузке настройки окон из *.wnd

Так вот, при событиях 2, 3 и 4 getDataSourceInfo(), вызванная из Init() выдаст все корректную информацию и о новом тайм-фрейме, и новом инструменте.
Причем в "Руководстве пользователя QLua" прямо сказано, что только именно событие 4 гарантирует корректную работу getDataSourceInfo() при вызове из Init()

А вот в случае события 1, по непонятной причине, от getDataSourceInfo() мы можем получить только текущий тайм-фрейм окна графика.

Так чем же событие 1 так уж принципиально отличается от других, в особенности от события 4?
Почему во 2-4 случаях quik "подключает" новый инструмент к индикатору до вызова Init(), в случае 1 - после?
 
Простите, про событие 2 я погорячился, Init() не вызывается. Но сути дела это не меняет
 
Цитата
Николай Камынин написал:
Цитата
А в квике как мультике про простоквашено (Письмо дяди Федора).
Начинал писать квик один писатель потом второй и т д.
Стратегию построения КВИКА разработали еще в прошлом веке.
Вот и нет однообразия.
А читатели - это клиенты брокеров.
Ну в принципе, это многое объясняет :)
 
Вы говорили не об этом, но правильно ли я понял, что если код такой:
Код
int = getDataSourceInfo().interval
, то интервал будет получаться на каждой свечке.
А если такой:
Код
if index == 1 then
 int = getDataSourceInfo().interval
end
, то интервал будет получен единожды?
 
Цитата
Русский написал:
Вы говорили не об этом, но правильно ли я понял, что если код такой:
Код
  int  =   getDataSourceInfo ().interval  
, то интервал будет получаться на каждой свечке.
А если такой:
Код
   if  index  =  =   1   then 
 int  =   getDataSourceInfo ().interval
 end   
, то интервал будет получен единожды?
Да.
Но уточню:
getDataSourceInfo ().interval - это тайм-фрейм окна графика, в котором выводится индикатор. Это, строго говоря, не обязательно интервал по времени между предыдущей и текущей свечками, т.к. данные для каких-то моментов времени могут отсутствовать, и тогда предыдущая свечка может отстоять от текущей на больший, чем тайм-фрейм промежуток времени.
Проверять тайм-фрейм для каждой свечки не имеет смысла, т.к. если Вы смените тайм-фрейм, то quik перезапустит цикл вызовов OnCalculate() и первым делом вызовет OnCalculate(index = 1)
 
Алексей, благодарю.
 
Цитата
Sergey Gorokhov написал:
Здравствуйте,
Задача вполне решается проверкой первой свечи в OnCalculate
Из приведенного описания не вполне понятно, чем данный подход не устраивает.
Здравствуйте.
Расскажу чем не устраивает.. или подскажите как такое реализовать: обновление Settings из кода OnCalculate.

хочу в настройки по своей внутренней формуле вывести номер свечи от которой буду производить отрисовку. При этом пользователь имеет право вручную изменить номер свечи.
также хочу вывести в настройки некое значение рассчитанное из данных getDataSourceInfo (дать возможность пользователю изменить заранее рассчитанные значения) ,например, маржинальный диапазон или базовый интервал.

да можно рассчитать на первой свече все данные, но не записать их в Settings
function OnCalculate(i)

  -- определяем текущую бумагу и ее характеристики
  -- в Init можно разместить, но нужн перезапускать рабочее место
  if (i == 1) then
    sec_code   = getDataSourceInfo().sec_code
    class_code = getDataSourceInfo().class_code
    interval   = getDataSourceInfo().interval
    param      = getDataSourceInfo().param
          -- не прокатит!!!!
    Settings.Fx= F(sec_code, class_code, interval, param)


на текущий момент не представляю как такое сделать. по все той же причине нельзя изменять Settings вне Init.

Подскажите как реализовать описанное выше   в индикаторе.

п.с. перезагружать рабочее место или изменять настройки в индикаторе и затем еще раз его запускать... ну такое
 
Цитата
Ilya написал:
Цитата
Sergey Gorokhov написал:
Здравствуйте,
Задача вполне решается проверкой первой свечи в OnCalculate
Из приведенного описания не вполне понятно, чем данный подход не устраивает.
Здравствуйте.
Расскажу чем не устраивает.. или подскажите как такое реализовать: обновление Settings из кода OnCalculate.

хочу в настройки по своей внутренней формуле вывести номер свечи от которой буду производить отрисовку. При этом пользователь имеет право вручную изменить номер свечи.
также хочу вывести в настройки некое значение рассчитанное из данных getDataSourceInfo (дать возможность пользователю изменить заранее рассчитанные значения) ,например, маржинальный диапазон или базовый интервал.

да можно рассчитать на первой свече все данные, но не записать их в Settings
function OnCalculate(i)

  -- определяем текущую бумагу и ее характеристики
  -- в Init можно разместить, но нужн перезапускать рабочее место
  if (i == 1) then
    sec_code   = getDataSourceInfo().sec_code
    class_code = getDataSourceInfo().class_code
    interval   = getDataSourceInfo().interval
    param      = getDataSourceInfo().param
          -- не прокатит!!!!
    Settings.Fx= F(sec_code, class_code, interval, param)


на текущий момент не представляю как такое сделать. по все той же причине нельзя изменять Settings вне Init.

Подскажите как реализовать описанное выше   в индикаторе.

п.с. перезагружать рабочее место или изменять настройки в индикаторе и затем еще раз его запускать... ну такое
Добрый день.

Извиняемся за долгий ответ.
Такой возможности нет, готовы зарегистрировать пожелание на доработку.
 
Прошло 5 лет!  Воз и ныне там, но это не точно. Пришлось изобрасти костыль. Итак, нам нужно в индикаторе в init получить sec_code. Для решения этого создал пустой индикатор, который в oncalculate на первой свече и единожды находит через  getDataSourceInfo() class_code и sec_code и помещает их в файл, базу данных тут кому как нравится, это вспомогательный индикатор кидаем на график жмем применить, получаем названия инструмента в базе данных, затем кидаем на график уже основной индикатор, который в init считывает полученное в первом. ВСЕ! Ниже пример костыля. Может разработчики или кто подкажет как все-таки решать такой простой вопрос, без костылей.
Код
local sqlite3 = require("lsqlite3")  -- Подключение SQLite (если доступно)

Settings = 
{
    Name = "!!!Current_Ticker",
    line = 
    {
        {
            Name = "FixedLine",
            Color = RGB(0, 255, 0),
            Type = TYPE_LINE,
            Width = 2
        }
    }
}

local db = nil
local saved = false  -- Флаг, чтобы выполнить запись только один раз

function Init()
    PrintDbgStr("!!!Current_Ticker: Init called")
    return 1  -- Количество линий
end

function OnCalculate(index)
    if index == 1 and not saved then
        PrintDbgStr("!!!Current_Ticker: OnCalculate first bar")

        local DSI = getDataSourceInfo()
        if DSI == nil then
            PrintDbgStr("!!!Current_Ticker: getDataSourceInfo() returned nil")
            return 10
        end

        local class_code = DSI.class_code
        local sec_code = DSI.sec_code

        PrintDbgStr("!!!Current_Ticker: class_code=" .. tostring(class_code) .. ", sec_code=" .. tostring(sec_code))

        local info = getSecurityInfo(class_code, sec_code)
        if info == nil then
            PrintDbgStr("!!!Current_Ticker: getSecurityInfo() returned nil")
            return 10
        end

        PrintDbgStr("!!!Current_Ticker: short_name=" .. tostring(info.short_name))

        db = sqlite3.open("C:\\QUIK\\LuaIndicators\\CurrentTicker.sqlite")
        if not db then
            PrintDbgStr("!!!Current_Ticker: Failed to open SQLite database")
            return 10
        end

        local create_table_sql = [[
            CRE ATE   TABLE IF NOT EXISTS CurrentTicker (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                class_code TEXT,
                sec_code TEXT,
                short_name TEXT
            );
        ]]
        db:exec(create_table_sql)
        PrintDbgStr("!!!Current_Ticker: Table CurrentTicker ensured")

        local stmt = db:prepare([[
            INS ERT IN TO CurrentTicker (class_code, sec_code, short_name)
            VALUES (?, ?, ?);
        ]])

        if stmt then
            stmt:bind_values(class_code, sec_code, info.short_name or "")
            stmt:step()
            stmt:finalize()
            PrintDbgStr("!!!Current_Ticker: Data inserted in CurrentTicker table")
        else
            PrintDbgStr("!!!Current_Ticker: Failed to prepare SQLite statement")
        end

        db:close()
        PrintDbgStr("!!!Current_Ticker: Database closed")

        saved = true
    end

    return 10
end

function OnDestroy()
    PrintDbgStr("!!!Current_Ticker: OnDestroy called")
    if db then
        db:close()
        PrintDbgStr("!!!Current_Ticker: Database closed on destroy")
    end
end
 
Артем,
Можете пояснить, почему надо именно в Init найти код инструмента.
 
Цитата
nikolz написал:
Артем ,
Можете пояснить, почему надо именно в Init найти код инструмента.
Потому что только в Init есть возможность программно изменять параметры Settings, далее во время нахождения индикатора на графике мы можем менять параметры вручную и программно только считывать их. Все бы ничего если бы в индикаторе было до 10 параметров, но если их 50-100, это превращается в издевательство.  
 
Цитата
Артем написал:
Цитата
nikolz написал:
 Артем  ,
Можете пояснить, почему надо именно в Init найти код инструмента.
Потому что только в Init есть возможность программно изменять параметры Settings, далее во время нахождения индикатора на графике мы можем менять параметры вручную и программно только считывать их. Все бы ничего если бы в индикаторе было до 10 параметров, но если их 50-100, это превращается в издевательство.  
Правильно Вас понял, что Вы меняете параметры у встроенных в QUIK индикаторов.
Поэтому так извращаетесь?.
вставьте функции нужных индикаторов в скрипт и меняйте все что хотите у них.
 
Относительно изменения Setting.
Это глобальная таблица и менять ее содержимое программно никто не запрещает.
Не знаю зачем это надо.
 
Артем,
Есть это:

OnChangeSettings

Функция вызывается при редактировании свойств индикатора после нажатия кнопок  «Применить» или «OK».  

Функция вызывается также при перезагрузке Рабочего места QUIK и при загрузке
wnd-файла либо tab-файла, в которых сохранен график с индикатором.

Формат вызова:  

OnChangeSettings()  

Пример:

Settings={Name="test1"}
function Init()
return 1
end
function OnChangeSettings()
message(Settings.Name)
end
 
Цитата
nikolz написал:
Относительно изменения Setting.
Это глобальная таблица и менять ее содержимое программно никто не запрещает.
Не знаю зачем это надо.
Это здорово, но можно пример кода в студию, как вы в индикаторе после добавления его на график ПРОГРАММНО поменяете Settings. Именно программно, без каких либо ручных манипуляций, например в зависмости от изменения цены инструмента.
 
Цитата
nikolz написал:
Артем ,
Есть это:

OnChangeSettings
      Функция вызывается при редактировании свойств индикатора после нажатия кнопок  «Применить» или «OK».      Функция вызывается также при перезагрузке Рабочего места QUIK и при загрузке
wnd-файла либо tab-файла, в которых сохранен график с индикатором.     Формат вызова:    OnChangeSettings()    Пример:  Settings={Name="test1"}
function Init()
return 1
end
function OnChangeSettings()
message(Settings.Name)
end
Тут так и написано, прям дословно, Settings можно изменить руками, ну или приведите пример кода, как программно поменять Settings и нажать кнопку применить или ок. Еще один бесплатый лайфхак к этому мануалу. - "Функция вызывается также при перезагрузке Рабочего места QUIK и при загрузке" и при "Cоздать копию" графика,да так можно делать, но если терминал грузится 10 минут, во время торговой сессии, это такое себе занятие, откровенная глупость, а если надо 10 раз поменять парметр (подобрать его, посмотреть результат), остается только удалить добавить индикатор...
 
Цитата
nikolz написал:
Цитата
Артем написал:
 
Цитата
nikolz  написал:
  Артем   ,
Можете пояснить, почему надо именно в Init найти код инструмента.
 Потому что только в Init есть возможность программно изменять параметры Settings, далее во время нахождения индикатора на графике мы можем менять параметры вручную и программно только считывать их. Все бы ничего если бы в индикаторе было до 10 параметров, но если их 50-100, это превращается в издевательство.  
Правильно Вас понял, что Вы меняете параметры у встроенных в QUIK индикаторов.
Поэтому так извращаетесь?.
вставьте функции нужных индикаторов в скрипт и меняйте все что хотите у них
Встроенные индикаторы, меня не интересуютот слова совсем, у меня своих достаточно. Рассмотрим простой пример, есть индикатор в котором используется дневной ATR , как параметр. Покажите как программно изменить этот параметр в индикаторе при наступлении следующего дня. Можно зайти и руками поправить (супер), но становится грустно когда графиков штук 50, нужно найти этот ATR для каждого инструмента и забить ручками, при этом человеческий фактор никто не отменял. Здесь становится намного проще просто удалить-добавить индикатор, но тоже проделать данную операцию 20 -30 раз, такое себе.
 
Цитата
Артем написал:
Цитата
nikolz написал:
 
Цитата
Артем  написал:
 
Цитата
 nikolz   написал:
   Артем    ,
Можете пояснить, почему надо именно в Init найти код инструмента.
  Потому что только в Init есть возможность программно изменять параметры Settings, далее во время нахождения индикатора на графике мы можем менять параметры вручную и программно только считывать их. Все бы ничего если бы в индикаторе было до 10 параметров, но если их 50-100, это превращается в издевательство.  
 Правильно Вас понял, что Вы меняете параметры у встроенных в QUIK индикаторов.
Поэтому так извращаетесь?.
вставьте функции нужных индикаторов в скрипт и меняйте все что хотите у них
Встроенные индикаторы, меня не интересуютот слова совсем, у меня своих достаточно. Рассмотрим простой пример, есть индикатор в котором используется дневной ATR , как параметр. Покажите как программно изменить этот параметр в индикаторе при наступлении следующего дня. Можно зайти и руками поправить (супер), но становится грустно когда графиков штук 50, нужно найти этот ATR для каждого инструмента и забить ручками, при этом человеческий фактор никто не отменял. Здесь становится намного проще просто удалить-добавить индикатор, но тоже проделать данную операцию 20 -30 раз, такое себе.
Могу показать, если выложите скрипт И на примере подробно расскажите .  
 
Итак, вот простой индикатор уровней, алгоритм обработки может быть любой в OnCalculate. Описание вопроса, индикатор имеет 40-50 параметров, параметры хранятся в базе данных для каждого конкретного тикера. Требуется при изменении цены или при срабанывании триггера получить параметры из базы и обратно записать в базу вычисленные параметры в индикаторе, сейчас это реализовано через OnChangeSettings (проверка параметра A1_SaveSettings установили в 1 нажали применить = записали в базу, скинули в 0 больше не пишется), это этап сохранения параметров в базу. А вот этап чтения параметров из базы тут все совсем грустно. Предположим простую ситуацию параметры поменялись в базе данных (пользователь внес данные в базу или сторонний софт провел расчет и внес параметры в базу данных). Нужно чтобы новые параметры были загружены индикатором. И я знаю только варианты 1) Удалить- добавить индикатор на график 2) Создать копию графика 3) Перезапустить Quik. Но что характерно, все эти манипуляции сводятся к одной цели запустить Init, согласитесь, это не дружественная ситуация, почему нет просто кнопки рестарт-реинит для конкретного графика? Но и тут нас ждет сюрприз просто так запустить инит не получится так как Quik забыл с каким инструментом работает! нужно полезть в базу данных и получить из псевдо курсора название инструмента, затем по полученному значению извлечь в инит параметры из основной базы для данного тикера! Итого два вопроса, 1) Как ПРОГРАММНО перевызвать Init или аналог его чтобы заполнить Settings ? (программный рестарт) 2) Почему нельзя прото добавить кнопку рестарт-реинит индикатора?(ручной рестарт, и избавить пользователей от опрации удали-добавь индикатор)
Код
local sqlite3 = require("lsqlite3")

if not PrintDbgStr then
    function PrintDbgStr(s) print(s) end
end

Settings = {
    Name = "!!!DB_Levels_v1",
    A1_Ticker = "sec_code [class_code]",
    A1_Enable = 1,
    A1_SaveSettings = 0, -- Добавлен параметр
    A1_target1 = 0.1,    A1_target2 = 0.1,    A1_target3 = 0.1,    A1_target4 = 0.1,    A1_target5 = 0.1,    A1_target6 = 0.1,
    Profile1 = 0.1,    Profile2 = 0.1,
    C1_Level1 = 0.1,    C1_Level2 = 0.1,    C1_Level3 = 0.1,    C1_Level4 = 0.1,    C1_Level5 = 0.1,    C1_Level6 = 0.1,    C1_Level7 = 0.1,    C1_Level8 = 0.1,    C1_Level9 = 0.1,
    C1_Level10_AlertFlag = 0,    C1_Level10 = 0.1,    C1_Level11 = 0.1,    C1_Level12 = 0.1,    C1_Level13 = 0.1,    C1_Level14 = 0.1,    C1_Level15 = 0.1,
    C1_Level16 = 0.1,    C1_Level17 = 0.1,    C1_Level18 = 0.1,    C1_Level19 = 0.1,    C1_Level20 = 0.1,    C1_Level21 = 0.1,    C1_Level22 = 0.1,    C1_Level23 = 0.1,
    C1_Level24 = 0.1,    C1_Level25 = 0.1,    C1_Level26 = 0.1,    C1_Level27 = 0.1,    C1_Level28 = 0.1,    C1_Level29 = 0.1,
    CD1_Level30 = 0.1,    CD1_Level31 = 0.1,    CD1_Level32 = 0.1,    CD1_Level33 = 0.1,    CD1_Level34 = 0.1,    CD1_Level35 = 0.1,    CD1_Level36 = 0.1,    CD1_Level37 = 0.1,    CD1_Level38 = 0.1,    CD1_Level39 = 0.1,
    CD1_Level30_2 = 0.1,C1_Level19_2 = 0.1,    DD1_Volume = 0.1,
    PER = "", DATE = "", TIME = "",
    line = {
        { Name="A1_target1", Color = RGB(255,0, 0), Type = TYPE_LINE, Width = 6  },
        { Name="A1_target2", Color = RGB(255,0, 0), Type = TYPE_LINE, Width = 6 },
        { Name="A1_target3", Color = RGB(255,0, 0), Type = TYPE_LINE, Width = 6 },
        { Name="A1_target4", Color = RGB(0,128, 64), Type = TYPE_LINE, Width = 6 },
        { Name="A1_target5", Color = RGB(0,128, 64), Type = TYPE_LINE, Width = 6 },
        { Name="A1_target6", Color = RGB(0,128, 64), Type = TYPE_LINE, Width = 6 },
        { Name="Profile1", Color = RGB(149, 142, 14), Type = TYPE_BARS, Width = 2 },
        { Name="Profile2", Color = RGB(149, 142, 14), Type = TYPE_BARS, Width = 2 },
        { Name="C1_Level1", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level2", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level3", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level4", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level5", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level6", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level7", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level8", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level9", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level10_AlertFlag", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level10", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level11", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
      { Name="C1_Level12", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
      { Name="C1_Level13", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level14", Color = RGB(255,128, 64), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level15", Color = RGB(128,128, 192), Type = TYPE_DASHDOT, Width = 1 },
        { Name="C1_Level16", Color = RGB(128,128, 192), Type = TYPE_DASHDOT, Width = 1 },
        { Name="C1_Level17", Color = RGB(128,128, 192), Type = TYPE_DASHDOT, Width = 1 },
        { Name="C1_Level18", Color = RGB(128,128, 192), Type = TYPE_DASHDOT, Width = 1 },
        { Name="C1_Level19", Color = RGB(128,128, 192), Type = TYPE_DASHDOT, Width = 1 },
        { Name="C1_Level20", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level21", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level22", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level23", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level24", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level25", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level26", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level27", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level28", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="C1_Level29", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level30", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level31", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level32", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level33", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level34", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level35", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level36", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level37", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level38", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level39", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="CD1_Level30_2", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
      { Name="C1_Level19_2", Color = RGB(255,0, 255), Type = TYPE_LINE, Width = 2 },
        { Name="DD1_Volume", Color = RGB(128,64, 64), Type = TYPE_DASHDOT, Width = 3 }
    }
}

local function get_db_path()
    local script_path = getScriptPath and getScriptPath() or "."
    local sep = package.config:sub(1,1)
    if script_path:sub(-1) ~= sep then script_path = script_path .. sep end
    local db_path = script_path .. "data.sqlite"
    PrintDbgStr("get_db_path: db_path = " .. db_path)
    return db_path
end

local db_fields = {
    "TICKER", "PER", "DATE", "TIME",
    "A1_target1", "A1_target2", "A1_target3", "A1_target4", "A1_target5", "A1_target6",
    "Profile1", "Profile2",
    "C1_Level1", "C1_Level2", "C1_Level3", "C1_Level4", "C1_Level5", "C1_Level6", "C1_Level7", "C1_Level8", "C1_Level9",
    "C1_Level10_AlertFlag", "C1_Level10", "C1_Level11", "C1_Level12", "C1_Level13", "C1_Level14", "C1_Level15",
    "C1_Level16", "C1_Level17", "C1_Level18", "C1_Level19", "C1_Level20", "C1_Level21", "C1_Level22", "C1_Level23",
    "C1_Level24", "C1_Level25", "C1_Level26", "C1_Level27", "C1_Level28", "C1_Level29",
    "CD1_Level30", "CD1_Level31", "CD1_Level32", "CD1_Level33", "CD1_Level34", "CD1_Level35", "CD1_Level36", "CD1_Level37", "CD1_Level38", "CD1_Level39",
    "CD1_Level30_2", "C1_Level19_2","DD1_Volume"
}

function OnChangeSettings()
    PrintDbgStr("OnChangeSettings: start")

    if Settings.A1_SaveSettings ~= 1 then
        PrintDbgStr("OnChangeSettings: A1_SaveSettings ~= 1, exiting")
        return
    end

    local db_path = get_db_path()
    PrintDbgStr("OnChangeSettings: opening database: " .. db_path)
    local db = sqlite3.open(db_path)
    if not db then
        PrintDbgStr("OnChangeSettings: failed to open database: " .. db_path)
        return
    end

    local ticker = Settings.A1_Ticker
    Settings.TICKER = ticker
    PrintDbgStr("OnChangeSettings: current ticker: " .. tostring(ticker))

    -- Проверяем, есть ли запись с таким тикером
    local sql_check = string.format("SEL ECT Id FR OM data WHERE TICKER = '%s' LIMIT 1", ticker)
    local existing_id = nil
    for row in db:nrows(sql_check) do
        existing_id = row.Id
        PrintDbgStr("OnChangeSettings: found existing record with Id = " .. tostring(existing_id))
        break
    end

    if existing_id then
        -- Обновляем существующую запись
        PrintDbgStr("OnChangeSettings: updating record with Id = " .. tostring(existing_id))
        local set_parts = {}
        for _, field in ipairs(db_fields) do
            if field ~= "TICKER" then
                local val = Settings[field]
                local val_str
                if type(val) == "number" then
                    val_str = tostring(val)
                else
                    val_str = "'" .. tostring(val):gsub("'", "''") .. "'"
                end
                table.insert(set_parts, string.format("%s = %s", field, val_str))
            end
        end
        local sql_update = string.format("UPD ATE data SE T %s WHERE Id = %d", table.concat(set_parts, ", "), existing_id)
        PrintDbgStr("OnChangeSettings: executing UPDATE: " .. sql_update)
        local rc = db:exec(sql_update)
        if rc ~= sqlite3.OK then
            PrintDbgStr("OnChangeSettings: upd ate failed: " .. db:errmsg())
        else
            PrintDbgStr("OnChangeSettings: successfully updated record for ticker " .. ticker)
        end
    else
        -- Вставляем новую запись
        PrintDbgStr("OnChangeSettings: record not found, inserting new")
        local columns = {}
        local values = {}
        for _, field in ipairs(db_fields) do
            table.insert(columns, field)
            local val = Settings[field]
            if type(val) == "number" then
                table.insert(values, tostring(val))
            else
                table.insert(values, "'" .. tostring(val):gsub("'", "''") .. "'")
            end
        end
        local sql_insert = string.format("INS ERT INTO data (%s) VALUES (%s)", table.concat(columns, ", "), table.concat(values, ", "))
        PrintDbgStr("OnChangeSettings: executing INSERT: " .. sql_insert)
        local rc = db:exec(sql_insert)
        if rc ~= sqlite3.OK then
            PrintDbgStr("OnChangeSettings: insert failed: " .. db:errmsg())
        else
            PrintDbgStr("OnChangeSettings: successfully inserted new record for ticker " .. ticker)
        end
    end

    PrintDbgStr("OnChangeSettings: finished")
    db:close()
end

-- Остальной код (Init, LoadSettingsFromDB, OnCalculate) без изменений



function LoadSettingsFromDB(a1_ticker)
    PrintDbgStr("LoadSettingsFromDB: start for A1_Ticker = " .. tostring(a1_ticker))
    local db_path = get_db_path()
    local db = sqlite3.open(db_path)
    if not db then
        PrintDbgStr("LoadSettingsFromDB: failed to open database: " .. db_path)
        return
    end
    local sql = string.format("SEL ECT * FR OM data WH ERE TICKER = '%s' LIMIT 1", a1_ticker)
    PrintDbgStr("LoadSettingsFromDB: sql = " .. sql)
    for row in db:nrows(sql) do
        for k, v in pairs(row) do
            if Settings[k] ~= nil and type(Settings[k]) == "number" then
                local num = tonumber(v)
                if num then
                    Settings[k] = num
                    PrintDbgStr("LoadSettingsFromDB: loaded " .. k .. " = " .. tostring(Settings[k]))
                end
            end
        end
        break
    end
    db:close()
    PrintDbgStr("LoadSettingsFromDB: done")
end

local function get_currentticker_db_path()
    local script_path = getScriptPath and getScriptPath() or "."
    local sep = package.config:sub(1,1)
    if script_path:sub(-1) ~= sep then script_path = script_path .. sep end
    return script_path .. "CurrentTicker.sqlite"
end

function GetLastTickerFromCurrentTicker()
    local db_path = get_currentticker_db_path()
    local db = sqlite3.open(db_path)
    if not db then
        PrintDbgStr("GetLastTickerFromCurrentTicker: failed to open database: " .. db_path)
        return nil, nil
    end

    local sql = "SELE CT sec_code, class_code FR OM CurrentTicker ORDER BY id DESC LIMIT 1"
    local sec_code, class_code = nil, nil
    for row in db:nrows(sql) do
        sec_code = row.sec_code
        class_code = row.class_code
        break
    end
    db:close()
    return sec_code, class_code
end


function Init()
    PrintDbgStr("Init: start (CurrentTicker version)")
    local sec_code, class_code = GetLastTickerFromCurrentTicker()
    if sec_code and class_code then
        local template = sec_code .. " [" .. class_code .. "]"
        Settings.A1_Ticker = template
        PrintDbgStr("Init: A1_Ticker se t to: " .. Settings.A1_Ticker)
        LoadSettingsFromDB(Settings.A1_Ticker)
    else
        PrintDbgStr("Init: No ticker found in CurrentTicker, fallback to default")
        Settings.A1_Ticker = "sec_code [class_code]"
    end
    PrintDbgStr("Init: done")
    return #Settings.line
end

function OnCalculate(index)
    local E = Settings.A1_Enable or 1
    return
        E * Settings.A1_target1, E * Settings.A1_target2, E * Settings.A1_target3, E * Settings.A1_target4, E * Settings.A1_target5, E * Settings.A1_target6,
        E * Settings.Profile1, E * Settings.Profile2,
        E * Settings.C1_Level1, E * Settings.C1_Level2, E * Settings.C1_Level3, E * Settings.C1_Level4, E * Settings.C1_Level5, E * Settings.C1_Level6, E * Settings.C1_Level7, E * Settings.C1_Level8, E * Settings.C1_Level9,
        E * Settings.C1_Level10_AlertFlag, E * Settings.C1_Level10, E * Settings.C1_Level11, E * Settings.C1_Level13, E * Settings.C1_Level19,
        E * Settings.C1_Level15, E * Settings.C1_Level16, E * Settings.C1_Level17, E * Settings.C1_Level18, E * Settings.C1_Level19, E * Settings.C1_Level20,
        E * Settings.C1_Level21, E * Settings.C1_Level22, E * Settings.C1_Level23, E * Settings.C1_Level24, E * Settings.C1_Level25, E * Settings.C1_Level26, E * Settings.C1_Level27, E * Settings.C1_Level28, E * Settings.C1_Level29,
        E * Settings.CD1_Level30, E * Settings.CD1_Level31, E * Settings.CD1_Level32, E * Settings.CD1_Level33, E * Settings.CD1_Level34, E * Settings.CD1_Level35, E * Settings.CD1_Level36, E * Settings.CD1_Level37, E * Settings.CD1_Level38, E * Settings.CD1_Level39,
        E * Settings.CD1_Level30_2, E * Settings.C1_Level19, E * Settings.DD1_Volume
end
 
У Вас уровни могут в любой момент поменяться что ли? Я их раз в час рассчитываю отдельным скриптом (сервер) и клиенты (индикаторы) по факту раз в час смотрят в хранилище уровней внутри OnCalculate и берут оттуда ближайшие 2-4 верхних и нижних. В моём случае хранение через C++ структуры данных внутри одной DLL, которую грузят серверный скрипт и клиентские индикаторы.
 
Артем,
Пишу алгоритм своего решения вашей задачи:
Если понял ее не правильно, то уточните, что не так.
---------------------
Алгоритм:
1) Создаем файл , в котором записываем в строку : Имя инструмента, A1,A2,....An
где A1,A... - параметры наших индикаторов (уровней)
Т е в файле столько строк, сколько всего инструментов наблюдаем
Прим:  Можно сделать для каждого инструмента свой файл. Имя файла ==имя инструмента.
--------------------------
В опCalculate  на кажом тике:
1) читаем имя инструмента
2)Читаем из файла строку параметров индикаторов данного инструмента
3) Выводим на график индикаторы
 
 
Цитата
funduk написал:
У Вас уровни могут в любой момент поменяться что ли? Я их раз в час рассчитываю отдельным скриптом (сервер) и клиенты (индикаторы) по факту раз в час смотрят в хранилище уровней внутри OnCalculate и берут оттуда ближайшие 2-4 верхних и нижних. В моём случае хранение через C++ структуры данных внутри одной DLL, которую грузят серверный скрипт и клиентские индикаторы.
да, и у меня так, но в Settings у вас ничего не выводится , то есть налету поменять нет возможности, нужно менять в базе данных, и с лагом уровни прорисуются, вопрос в другом как программно изменить Settings, вне Init
 
Цитата
nikolz написал:
Артем ,
Пишу алгоритм своего решения вашей задачи:
Если понял ее не правильно, то уточните, что не так.
---------------------
Алгоритм:
1) Создаем файл , в котором записываем в строку : Имя инструмента, A1,A2,....An
где A1,A... - параметры наших индикаторов (уровней)
Т е в файле столько строк, сколько всего инструментов наблюдаем
Прим:  Можно сделать для каждого инструмента свой файл. Имя файла ==имя инструмента.
--------------------------
В опCalculate  на кажом тике:
1) читаем имя инструмента
2)Читаем из файла строку параметров индикаторов данного инструмента
3) Выводим на график индикаторы
совершенно не понятное решение, в моем скрипте все уже реализовано через базу данных, зачем какие то текстовые файлы? я предельно четко сформулировал 2 вопроса. Вот они : 1) Как ПРОГРАММНО перевызвать Init или аналог его чтобы заполнить Settings ? (программный рестарт) 2) Почему нельзя прото добавить кнопку рестарт-реинит индикатора?(ручной рестарт, и избавить пользователей от опрации удали-добавь индикатор)  
 
Цитата
Артем написал:
Цитата
nikolz написал:
 Артем  ,
Пишу алгоритм своего решения вашей задачи:
Если понял ее не правильно, то уточните, что не так.
---------------------
Алгоритм:
1) Создаем файл , в котором записываем в строку : Имя инструмента, A1,A2,....An
где A1,A... - параметры наших индикаторов (уровней)
Т е в файле столько строк, сколько всего инструментов наблюдаем
Прим:  Можно сделать для каждого инструмента свой файл. Имя файла ==имя инструмента.
--------------------------
В опCalculate  на кажом тике:
1) читаем имя инструмента
2)Читаем из файла строку параметров индикаторов данного инструмента
3) Выводим на график индикаторы
совершенно не понятное решение, в моем скрипте все уже реализовано через базу данных, зачем какие то текстовые файлы? я предельно четко сформулировал 2 вопроса. Вот они : 1) Как ПРОГРАММНО перевызвать Init или аналог его чтобы заполнить Settings ? (программный рестарт) 2) Почему нельзя прото добавить кнопку рестарт-реинит индикатора?(ручной рестарт, и избавить пользователей от опрации удали-добавь индикатор)  
Если не знаете, то файловая система это тоже база данных.
-----------------
Вы спросили как сделать так чтобы не руками без Init.
Я Вам написал как это сделать.
-----------------------
В моем решении нет надобности что-то делать руками и нет надобности вызывать init.
В моем решении нет Ваших проблем.
--------------------------
Про Ваш скрипт я лучше промолчу, чтобы вас не обижать.
 
Цитата
nikolz написал:
Цитата
Артем написал:
 
Цитата
nikolz  написал:
  Артем   ,
Пишу алгоритм своего решения вашей задачи:
Если понял ее не правильно, то уточните, что не так.
---------------------
Алгоритм:
1) Создаем файл , в котором записываем в строку : Имя инструмента, A1,A2,....An
где A1,A... - параметры наших индикаторов (уровней)
Т е в файле столько строк, сколько всего инструментов наблюдаем
Прим:  Можно сделать для каждого инструмента свой файл. Имя файла ==имя инструмента.
--------------------------
В опCalculate  на кажом тике:
1) читаем имя инструмента
2)Читаем из файла строку параметров индикаторов данного инструмента
3) Выводим на график индикаторы
 совершенно не понятное решение, в моем скрипте все уже реализовано через базу данных, зачем какие то текстовые файлы? я предельно четко сформулировал 2 вопроса. Вот они : 1) Как ПРОГРАММНО перевызвать Init или аналог его чтобы заполнить Settings ? (программный рестарт) 2) Почему нельзя прото добавить кнопку рестарт-реинит индикатора?(ручной рестарт, и избавить пользователей от опрации удали-добавь индикатор)  
Если не знаете, то файловая система это тоже база данных.
-----------------
Вы спросили как сделать так чтобы не руками без Init.
Я Вам написал как это сделать.
-----------------------
В моем решении нет надобности что-то делать руками и нет надобности вызывать init.
В моем решении нет Ваших проблем.
--------------------------
Про Ваш скрипт я лучше промолчу, чтобы вас не обижать.
"Если не знаете, то файловая система это тоже база данных." - это шедевр! А какой версии SQL там используется, СУБД тоже имеется? может,  что то упустил за 25 лет в ИТ.

Вопрос остается прежним, есть Settings как туда программно записать значения, ваше решение, возможно, рабочее, но пользователь не видит изменений.

Далее возможно два варианта:

1 Вообще не выводим Settings пользователю - получаем черный ящик, все изменения через корректировку файлов в второннем софте (просто супер решение, но в этом случае хотя бы отсутствуют протеворечивость данных).

2 Settings выводим и даже разрешаем править значения, пользователь руками правит параметры, и жмет применить, параметры поменялись, затем через некоторое время Settings загружается из файлов  и  результат: пользователь видит одни значения, а по факт у значения совсем другие, или наоборот поменяли настройки в файле, а в окошке Settings ни чего не меняется.
В этом варианте возникает противоречивость данных.

Я как раз про это и говорю, и прошу показать как можно решить этоту задачу. Устранить потенциальную опасность противоречия данных.

По второму вопросу вообще нет решения, как избавить пользователя  от "удали-добавь индикатор, перезапусти Quik" ?
 
Цитата
Артем написал:
да, и у меня так, но в Settings у вас ничего не выводится , то есть налету поменять нет возможности, нужно менять в базе данных, и с лагом уровни прорисуются, вопрос в другом как программно изменить Settings, вне Init
Если на каком-то одном инструменте надо было вручную добавить несколько уровней, я использовал вкладку "Уровни" для цены инструмента в настройках графика.

В чём вопрос я понимаю, но считаю, что так делать - это противоестественно. Settings для статических настроек, туда ничего не выводится, динамические внутри скрипта должны работать. В TradingView так же, кстати. Не слышал о системах, где было бы иначе. Зачем в Settings выводить численно значения уровней, если они и так на графике видны? Если override нужен, ну так только его в Settings легко оставить - если в поле ноль, возвращать автоматический уровень (который берётся не из Settings, а внутри OnCalculate), если не ноль, то возвращать это ненулевое значение
 
Цитата
funduk написал:
Цитата
Артем написал:
да, и у меня так, но в Settings у вас ничего не выводится , то есть налету поменять нет возможности, нужно менять в базе данных, и с лагом уровни прорисуются, вопрос в другом как программно изменить Settings, вне Init
Если на каком-то одном инструменте надо было вручную добавить несколько уровней, я использовал вкладку "Уровни" для цены инструмента в настройках графика.

В чём вопрос я понимаю, но считаю, что так делать - это противоестественно. Settings для статических настроек, туда ничего не выводится, динамические внутри скрипта должны работать. В TradingView так же, кстати. Не слышал о системах, где было бы иначе. Зачем в Settings выводить численно значения уровней, если они и так на графике видны? Если override нужен, ну так только его в Settings легко оставить - если в поле ноль, возвращать автоматический уровень (который берётся не из Settings, а внутри OnCalculate), если не ноль, то возвращать это ненулевое значение
Да я же не спорю, я предлагаю сделать улучшение, расширить возможности Quik - избавить пользователя он коронного и разящего наповал  далее цитата из мануала
getDataSourceInfo
Функция возвращает таблицу Lua с параметрами:
ВАЖНО! Для корректной работы функции getDataSourceInfo, вызываемой из
функции Init, необходимо перезапустить Рабочее место QUIK после добавления
индикатора на график.

С этого и начался весь спор, нужно добавить в Quik кнопку рестарта текущего индикатора, тут я вообще не вижу сложностей (удали индикатор с графика и вставь снова с теми же параметрами выполнится инит), + решить вопрос по getDataSourceInfo в Init. Эти два простых действия сделают интерфейс дружественным.

Цитата: "В чём вопрос я понимаю, но считаю, что так делать - это противоестественно. Settings для статических настроек"
Здесь как раз то и есть нюанс, что статистика не стоит на месте , и плавно изменяется, ежедневно а у когото и ежетиково , нужно руками корректировать уже приводил такой простой параметр ATR, да его можно рассчитывать в индикаторе, но можно просто хранить в базе параметром и  тащить из базы, не показывая пользователю, а можно улучшить Quik, и расширить его возможности до изменять Setings программно. :)

Обойти отсутствие повторного запуска Init без ручных манипуляций я не смог, поэтому и прошу помощи. Мне прото жутко надоело каждый раз удали-добавь индикатор, может я чего-то не знаю... Может есть более протой путь.

Что есть реально другой путь кроме удали-добавь индикатор? Например, при написании кода того же индикатора, он меняется по 10 раз в день, какждый раз я делаю удалил-добавил, можно как то иначе?
 
Цитата
Артем написал:
можно как то иначе?
   В скрипте можно создать любые индикаторы (с полным контролем над ними) с использованием источников (DS), полученных функцией CreateDataSource. Кроме того в ветке https://forum.quik.ru/messages/forum10/message76052/topic5466/#message76052 представлен модуль подключения в скрипте к индикаторам папки LuaIndicators с готовыми индикаторами с сайта разработчика QUIK.
 
Цитата
Артем написал:
Что есть реально другой путь кроме удали-добавь индикатор? Например, при написании кода того же индикатора, он меняется по 10 раз в день, какждый раз я делаю удалил-добавил, можно как то иначе?
В режиме связанных окон (допустим таблица текущих торгов + график), при переходах с тикера на тикер, индикатор будет обновляться в месте с графиком, получая данные и пересчитывая значения.
Также заметил в Вашем примере ошибки с типом данных, если на нем экспериментируете лучше поправить, пробелы в именах, линей меньше выводит.  
 
Цитата
Артем написал:
Цитата
ь.
"Если не знаете, то файловая система это тоже база данных." - это шедевр! А какой версии SQL там используется, СУБД тоже имеется? может,  что то упустил за 25 лет в ИТ.
Ликбез:
-------------------
Базы данных (БД) бывают разных типов, которые отличаются структурой и областью применения.
Среди основных видов — реляционные, нереляционные, иерархические и сетевые, а также простейшие.
-----------------------
Базы данных на основе файлов (flat-file databases) — это простой метод хранения данных в текстовом файле.
В отличие от традиционных реляционных баз данных, в которых используются сложные структуры с таблицами, строками и столбцами, база данных с плоскими файлами организует данные линейным и последовательным образом.
Особенности:
  • Каждая строка в файле представляет одну запись.
  • Отдельные поля внутри записи обычно разделяются разделителями, такими как запятые или табуляции.
  • Нет структур для индексирования или распознавания связей между записями.
Виды
Базы данных с плоскими файлами подходят для небольших приложений и временного хранения данных. Некоторые случаи использования:
  • файлы конфигурации в программных приложениях;
  • обмен данными между различными системами;
  • файлы журналов, например, лог-файлы веб-сервера.
Форматы
Для хранения данных в базах данных с плоскими файлами используются, например:
  • CSV (значения, разделённые запятыми) — каждое поле разделяется запятой.
  • TSV (значения, разделённые табуляцией) — в качестве разделителей используются табуляции, что полезно, когда запятые — часть самих данных.
  • Формат фиксированной ширины — каждое поле занимает заранее определённое количество символов, выравнивая данные по столбцам.
Инструменты
Многие языки программирования и приложения имеют встроенную поддержку чтения и записи данных из баз данных с плоскими файлами. Например:
  • Программы для работы с электронными таблицами (Microsoft Excel, Google Sheets) — могут читать и работать с плоскими файлами.
  • Языки программирования (Python, Java) — могут анализировать и обрабатывать данные с помощью встроенных или сторонних библиотек.
Недостатки
Базы данных с плоскими файлами имеют и ограничения. Некоторые из них:
  • Ограниченная масштабируемость — по мере роста объёма данных производительность базы данных может снижаться.
  • Отсутствие согласованности данных — если нужно обновить несколько записей, это становится утомительной задачей и увеличивает вероятность несоответствий.
  • Сложность извлечения данных — из-за отсутствия поддержки структурированного языка запросов (SQL) запросы часто требуют ручного сканирования и фильтрации через записи.
  • Ограниченный одновременный доступ — базы данных с плоскими файлами не предназначены для одновременного доступа нескольких пользователей или приложений.
------------------------------------------

SQL ( Structured Query Language — «язык структурированных запросов») —  применяемый для создания, модификации и управления данными в реляционных базах данных.
-----------------------
Нереляционные базы данных
В нереляционных БД не используется табличная схема строк и столбцов. Применяется модель хранения, оптимизированная под конкретный тип данных.
Некоторые типы нереляционных БД:
  • Документоориентированные. Данные хранятся в виде документов в форматах JSON, BSON или XML.
  • Колоночные. Информация хранится не в строках, а в столбцах.
  • Графовые. Данные представлены в виде графов, что упрощает их хранение и поиск.
-  ---------------------------
Для работы с нереляционными базами данных (NoSQL) используются специализированные языки запросов. Это связано с особенностями моделей данных в таких базах: данные могут храниться в виде документов, колонок, графов или на основе пар «ключ-значение». Ниже приведены примеры языков запросов для работы с базами данных MongoDB, Cassandra, BigTable и GraphDB.


MongoDB
MongoDB Query Language (MQL) — язык запросов для документо-ориентированной базы данных MongoDB. Запросы формулируются как объекты JSON, что делает их интуитивно понятными. MQL поддерживает операции CRUD (создание, чтение, обновление и удаление), а также функции агрегирования для фильтрации, сортировки и группировки данных.

Cassandra
Cassandra Query Language (CQL) — основной язык запросов для распределённой базы данных Apache Cassandra. Синтаксис похож на SQL, но оптимизирован для принципов NoSQL: горизонтальной масштабируемости, высокой доступности и партиционированного хранения данных. CQL не поддерживает традиционные SQL-соединения, а поощряет денормализацию — дублирование данных по таблицам для эффективного запроса.

BigTable
Собственный язык запросов — BigTable не поддерживает язык запросов SQL. Запросы выполняются через API, который поддерживает ряд популярных языков программирования (Java, Python, C#, C++). Изначально нет схемы данных, но возможна поддержка пользовательской схемы.


GraphDB
Cypher — язык запросов для графовых баз данных, например, Neo4j. Cypher — декларативный язык, позволяет создавать, обновлять и удалять вершины, рёбра, метки и свойства, а также управлять индексами и ограничениями.
Другие языки запросов:
  • Gremlin — поддерживается базой данных Titan, позволяет выполнять базовые операции с элементами графа (создание, обновление и удаление вершин, рёбер, меток и свойств).
  • SPARQL — используется в базах данных RDF.
Страницы: 1
Читают тему
Наверх