Артем (Все сообщения пользователя)

Выбрать дату в календареВыбрать дату в календаре

Страницы: 1
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Цитата
nikolz написал:
Цитата
Артем написал:
 
Цитата
ь.
 "Если не знаете, то файловая система это тоже база данных." - это шедевр! А какой версии 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.
Благодарю за ликбез, действительно, я все время работаю с реляционными базами. И, после вашего замечания, полностью  согласен любой текстовый файл можно превратить в базу данных, так как у них одна сущность - хранение данных, а способы  ее организации  и обработки, конечно, могут быть различными.
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Цитата
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 раз в день, какждый раз я делаю удалил-добавил, можно как то иначе?
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Цитата
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" ?
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Цитата
nikolz написал:
Артем ,
Пишу алгоритм своего решения вашей задачи:
Если понял ее не правильно, то уточните, что не так.
---------------------
Алгоритм:
1) Создаем файл , в котором записываем в строку : Имя инструмента, A1,A2,....An
где A1,A... - параметры наших индикаторов (уровней)
Т е в файле столько строк, сколько всего инструментов наблюдаем
Прим:  Можно сделать для каждого инструмента свой файл. Имя файла ==имя инструмента.
--------------------------
В опCalculate  на кажом тике:
1) читаем имя инструмента
2)Читаем из файла строку параметров индикаторов данного инструмента
3) Выводим на график индикаторы
совершенно не понятное решение, в моем скрипте все уже реализовано через базу данных, зачем какие то текстовые файлы? я предельно четко сформулировал 2 вопроса. Вот они : 1) Как ПРОГРАММНО перевызвать Init или аналог его чтобы заполнить Settings ? (программный рестарт) 2) Почему нельзя прото добавить кнопку рестарт-реинит индикатора?(ручной рестарт, и избавить пользователей от опрации удали-добавь индикатор)  
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Цитата
funduk написал:
У Вас уровни могут в любой момент поменяться что ли? Я их раз в час рассчитываю отдельным скриптом (сервер) и клиенты (индикаторы) по факту раз в час смотрят в хранилище уровней внутри OnCalculate и берут оттуда ближайшие 2-4 верхних и нижних. В моём случае хранение через C++ структуры данных внутри одной DLL, которую грузят серверный скрипт и клиентские индикаторы.
да, и у меня так, но в Settings у вас ничего не выводится , то есть налету поменять нет возможности, нужно менять в базе данных, и с лагом уровни прорисуются, вопрос в другом как программно изменить Settings, вне Init
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Итак, вот простой индикатор уровней, алгоритм обработки может быть любой в 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
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Цитата
nikolz написал:
Цитата
Артем написал:
 
Цитата
nikolz  написал:
  Артем   ,
Можете пояснить, почему надо именно в Init найти код инструмента.
 Потому что только в Init есть возможность программно изменять параметры Settings, далее во время нахождения индикатора на графике мы можем менять параметры вручную и программно только считывать их. Все бы ничего если бы в индикаторе было до 10 параметров, но если их 50-100, это превращается в издевательство.  
Правильно Вас понял, что Вы меняете параметры у встроенных в QUIK индикаторов.
Поэтому так извращаетесь?.
вставьте функции нужных индикаторов в скрипт и меняйте все что хотите у них
Встроенные индикаторы, меня не интересуютот слова совсем, у меня своих достаточно. Рассмотрим простой пример, есть индикатор в котором используется дневной ATR , как параметр. Покажите как программно изменить этот параметр в индикаторе при наступлении следующего дня. Можно зайти и руками поправить (супер), но становится грустно когда графиков штук 50, нужно найти этот ATR для каждого инструмента и забить ручками, при этом человеческий фактор никто не отменял. Здесь становится намного проще просто удалить-добавить индикатор, но тоже проделать данную операцию 20 -30 раз, такое себе.
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Цитата
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 раз поменять парметр (подобрать его, посмотреть результат), остается только удалить добавить индикатор...
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Цитата
nikolz написал:
Относительно изменения Setting.
Это глобальная таблица и менять ее содержимое программно никто не запрещает.
Не знаю зачем это надо.
Это здорово, но можно пример кода в студию, как вы в индикаторе после добавления его на график ПРОГРАММНО поменяете Settings. Именно программно, без каких либо ручных манипуляций, например в зависмости от изменения цены инструмента.
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Цитата
nikolz написал:
Артем ,
Можете пояснить, почему надо именно в Init найти код инструмента.
Потому что только в Init есть возможность программно изменять параметры Settings, далее во время нахождения индикатора на графике мы можем менять параметры вручную и программно только считывать их. Все бы ничего если бы в индикаторе было до 10 параметров, но если их 50-100, это превращается в издевательство.  
Вызов getDataSourceInfo() из Init() в Lua индикаторах
 
Прошло 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
Все индикаторы на Lua
 
Цитата
Цитата
Смнаписал:
Цитата
написал:
Добрый день!
Подскажите где взять indicators.zip под lua 5.4. У меня часть индикаторов на 5.4. и вместе на одном графике они не уживаются с 5.3.
indicators.zip  Это какой-то архив?
Вы где его взяли?
Вы думаете, что все должны знать, что у Вас в этом архиве?
Смотрите пост 80.Там есть ссылка, но там только lua 5.3, который не работает вместе с 5.4. Один индикатор psar я починил заменой  unpack(arg) на  table.unpack(arg), но не все можно так просто починить, нужно к автору этих индикаторов обращаться. И было бы здорово переделать их под 5.4, а не чтобы каждый сам их прилаживал под себя. Хотя и так тоже наверное можно. Вот переделанный мной Psar под lua 5.4
Код
Settings = {
Name = "*PSAR (Parabolic SAR)", 
Step = 0.02,
MaxStep = 0.2, 
line = {{
      Name = "Horizontal line",
      Type = TYPE_LINE, 
      Color = RGB(140, 140, 140)
      },
      {
      Width = 3,
      Name = "PSAR_Up", 
      Type = TYPE_POINT, 
      Color = RGB(221, 44, 44)
      },
      {
      Width = 3,
      Name = "PSAR_Down", 
      Type = TYPE_POINT, 
      Color = RGB(0, 206, 0)
      }
      },
Round = "off",
Multiply = 1,
Horizontal_line="off"
}

function Init()
   func = PSAR()
   return #Settings.line
end

function OnCalculate(Index) 
local Out = ConvertValue(Settings, func(Index, Settings))
local HL = tonumber(Settings.Horizontal_line)
   if Out then
      if Out > ((H(Index)-L(Index))/2)+L(Index) then
         return HL,Out,nil
      else
         return HL,nil,Out
      end
   else
      return HL,nil,nil
   end
end

function PSAR() --Parabolic SAR ("PSAR")
   local tmp = {pp=nil, p=nil}
   local it = {ppp=0, pp=0, p=0, l=0}
return function (I, Fsettings, ds)
local Out = nil
local Fsettings=(Fsettings or {})
local Step = (Fsettings.Step or 0.02)
local MaxStep = (Fsettings.MaxStep or 0.2)
   if I == 1 then
      tmp = {pp=nil, p=nil}
      it = {ppp=0, pp=0, p=0, l=0}
   end
   if CandleExist(I,ds) then
      if I~=it.p then 
         it={ppp=it.pp, pp=it.p, p=I, l=it.l+1}
         tmp.pp = tmp.p
      end
      local cand = {ppp=nil, pp=nil, p=nil}
      tmp.p = {Val = nil, Step = 0, Ext = 0, Long = true}
      cand.p = {H = GetValueEX(it.p,HIGH,ds), L = GetValueEX(it.p,LOW,ds)}
      if it.l==2 then
         tmp.p = {Val = GetValueEX(it.p,HIGH,ds), Step = Step, Ext = cand.p.H, Long = true}
      end
      if it.l > 2 then
         local Revers = false
         tmp.p.Val = tmp.pp.Val + tmp.pp.Step * (tmp.pp.Ext - tmp.pp.Val)
         tmp.p.Long = tmp.pp.Long
         tmp.p.Ext = tmp.pp.Ext
         tmp.p.Step = tmp.pp.Step
         if tmp.pp.Long then
            if cand.p.L < tmp.p.Val then
               tmp.p = {Val = tmp.pp.Ext, Step = Step, Ext = cand.p.L, Long = false}
               Revers = true
            end
         else
            if cand.p.H > tmp.p.Val then
               tmp.p = {Val = tmp.pp.Ext, Step = Step, Ext = cand.p.H, Long = true}
               Revers = true
            end
         end
         if not Revers then
            cand.pp = {H = GetValueEX(it.pp,HIGH,ds), L = GetValueEX(it.pp,LOW,ds)}
            cand.ppp = {H = GetValueEX(it.ppp,HIGH,ds), L = GetValueEX(it.ppp,LOW,ds)}
            if tmp.pp.Long then
               if cand.p.H > tmp.pp.Ext then
                  tmp.p.Ext = cand.p.H
                  tmp.p.Step = tmp.pp.Step + Step
                  if tmp.p.Step > MaxStep then tmp.p.Step = MaxStep end
               end
               if cand.pp.L < tmp.p.Val then tmp.p.Val = cand.pp.L end
               if cand.ppp.L < tmp.p.Val then tmp.p.Val = cand.ppp.L end
            else
               if cand.p.L < tmp.pp.Ext then
                  tmp.p.Ext = cand.p.L
                  tmp.p.Step = tmp.pp.Step + Step
                  if tmp.p.Step > MaxStep then tmp.p.Step = MaxStep end
               end
               if cand.pp.H > tmp.p.Val then tmp.p.Val = cand.pp.H end
               if cand.ppp.H > tmp.p.Val then tmp.p.Val = cand.ppp.H end
            end
         end
      end
      return tmp.p.Val
   end
return nil
end
end

SMA,MMA,EMA,WMA,SMMA,VMA = "SMA","MMA","EMA","WMA","SMMA","VMA"
OPEN,HIGH,LOW,CLOSE,VOLUME,MEDIAN,TYPICAL,WEIGHTED,DIFFERENCE,ANY = "O","H","L","C","V","M","T","W","D","A"

function CandleExist(I,ds)
return (type(C)=="function" and C(I)~=nil) or
   (type(ds)=="table" and (ds[I]~=nil or (type(ds.Size)=="function" and (I>0) and (I<=ds:Size()))))
end

function Squeeze(I,P)
   return math.fmod(I-1,P+1)
end

function ConvertValue(T,...)
local function r(V, R) 
   if R and string.upper(R)== "ON" then R=0 end
   if V and tonumber(R) then
      if V >= 0 then return math.floor(V * 10^R + 0.5) / 10^R
      else return math.ceil(V * 10^R - 0.5) / 10^R end
   else return V end
end
local arg = {...}
arg.n = select('#', ...)
   if arg.n > 0 then
      for i = 1, arg.n do
         arg[i]=arg[i] and r(arg[i] * ((T and T.Multiply) or 1), (T and T.Round) or "off")
      end
      return table.unpack(arg)
   else return nil end
end


function GetValueEX(I,VT,ds) 
VT=(VT and string.upper(string.sub(VT,1,1))) or ANY
   if VT == OPEN then         --Open
      return (O and O(I)) or (ds and ds:O(I))
   elseif VT == HIGH then       --High
      return (H and H(I)) or (ds and ds:H(I))
   elseif VT == LOW then      --Low
      return (L and L(I)) or (ds and ds:L(I))
   elseif VT == CLOSE then      --Close
      return (C and C(I)) or (ds and ds:C(I))
   elseif VT == VOLUME then      --Volume
      return (V and V(I)) or (ds and ds:V(I)) 
   elseif VT == MEDIAN then      --Median
      return ((GetValueEX(I,HIGH,ds) + GetValueEX(I,LOW,ds)) / 2)
   elseif VT == TYPICAL then   --Typical
      return ((GetValueEX(I,MEDIAN,ds) * 2 + GetValueEX(I,CLOSE,ds))/3)
   elseif VT == WEIGHTED then   --Weighted
      return ((GetValueEX(I,TYPICAL,ds) * 3 + GetValueEX(I,OPEN,ds))/4) 
   elseif VT == DIFFERENCE then   --Difference
      return (GetValueEX(I,HIGH,ds) - GetValueEX(I,LOW,ds))
   else                     --Any
      return (ds and ds[I])
   end
return nil
end
Все индикаторы на Lua
 
Добрый день!
Подскажите где взять indicators.zip под lua 5.4. У меня часть индикаторов на 5.4. и вместе на одном графике они не уживаются с 5.3.
Экспорт ежедневных цен за прошедшие 365 дней, Возможно ли выгрузить и от кого зависит - системы или брокера
 
В продолжении этой темы есть вопрос: Есть ли подобная процедура функция на Lua?
Пользователи Telegram-Bot! Как организовать работу через proxy?
 
Я поставил OpenVPN и купил платный доступ за $1 в месяц.
Вопрос по обработке исключений в Qlua, Вопрос по обработке исключений в Qlua
 
C пиногом разобрался путем

function send_telegram() --если true, то слать телеграм
local handler = io.popen("chcp 861 && ping -n 1 api.telegram.org")
local response = handler:read("*a")
start_pos, end_pos = string.find(response, "Lost = 0" )
if start_pos==nil then telegram=false
  else telegram=true
end
return telegram
end

Все бы ничего, при вызове  io.popen каждый раз кратковременно командное окошко всплывает и перехватывает фокус ввода, то есть Quik на 1 -2 сек становится не главным окном, что очень не хорошо. Как это можно победить запихать вызов пингов в фоновое окно? Может кто уже побеждал?  
Вопрос по обработке исключений в Qlua, Вопрос по обработке исключений в Qlua
 
Добрый день!
Столкнулся с тавой проблемой в Qlua :

Есть следующий код, который успешно работает при отсутствии ошибки.

require"QL"
require 'luanet'
require 'socket'
--вывод в телеграм
luanet.load_assembly "System"
WebClient = luanet.import_type("System.Net.WebClient")
wc = WebClient()
a="Start%20program%20at%20*" .. os.date("%H:%M:%S") .. "*"
PrintDbgStr(a)
wc:DownloadString("https://api.telegram.org/botХХХХХХХХХХХ/sendMessage?chat_id=ХХХХХ..." .. a .. "&parse_mode=Markdown") --значения поменял вывод сообщений в телеграм

Суть кода выводить сообщения в телеграм. До недавного времени все работало успешно, теперь когда РКН блокирует сообщения. QUIK ПОЛНОСТЬЮ ВЫЛЕТАЕТ.
Так как отсутствие коннекта к телеграму предсказать невозможно, то не возможно предсказать, когда вылетит терминал Quik. Нужно искать выход - обработать исключение!

Попробовал использовать

local status, err = pcall(function () www=wc:DownloadString(b .."https://api.telegram.org/botХХХХХХХХХХХХ/sendMessage?chat_id=ХХХХХ...) end)

if err==nil then err="" end -- до этого места доходит пока нет ошибок и в дебагер выдает нормальную информацию
PrintDbgStr(tostring(status) .. " --- " .. err .. " --- " .. www) -- если ошибка в ссылке или тайаут, то квик вылетает и информации в дебагере нет!!!

Вопрос КАК заставить QLUA обработать исключение.
Виды исключений : неправильная ссылка, не верный адрес, хост не доступен???

Проверял послюднюю часть кода в ZeroBrane Studio для Lua - успенно отрабатывает возвращает код ошибки и я могу его обработать.
Помогите разобоаться
Страницы: 1
Наверх