Функция main выполняется в отдельном потоке, в этом весь смысл, чтобы его понять лучше, запустите вот такой скрипт:
1
2
3
| while true do
sleep(10000)
end |
Попробуйте что-то сделать в терминале после запуска такого скрипта. Ваш терминал будет "висеть" 10 секунд, потому что цикл while будет выполняться в основном потоке терминала, в том же где происходит все взаимодействие с пользователем, т.е. с Вами.
Потом запустите вот такой скрипт:
1
2
3
4
5
| function main()
while true do
sleep(10000)
end
end |
Здесь тот же самый цикл, только уже внутри main, после запуска такого скрипта терминал будет работать как обычно, как будто никакого скрипта и не выполняется, потому что для функции main терминал создаст отдельный поток, параллельный основному. Чтобы понять что такое потоки, можете сравнить их с рабочими на фабрике, один рабочий - один поток. Представьте, что у Вас есть один рабочий, который занимается отрисовкой всего в терминале, он все отрисовывает, все работает, все хорошо. И тут Вы ему говорите: "Выполни мой скрипт", он говорит: "Хорошо", бросает отрисовку, начинает выполнять Ваш скрипт и видит там команду sleep(10000), для него это означает "ничего не делай 10 секунд", он говорит: "хорошо", и просто сидит на стуле 10 секунд 🙂 Естественно никто в это время ничего в терминале не перерисовывает и не реагирует на действия пользователя. Посидел работник 10 секунд и дальше продолжил заниматься отрисовкой, терминал "ожил". А вот если Вы ему "скажите" выполнить второй скрипт с main, то он, увидев main, подзывает другого работника, который ничего не делал и перекладывает эту задачу на него, а сам продолжает заниматься отрисовкой, т.к. наш первый работник у них главный, он может другим задачи распределять 🙂 Второй работник начинает выполнять все, что находится в функции main и никак не отвлекает "бригадира" от его работы.
Сразу могу провести аналогию с функциями обратного вызова. Если, например, мы запустили такой скрипт:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| run = true
function main()
while run do
sleep(10)
end
message('Пока')
end
function OnTrade()
message('Появилась новая сделка')
end
function OnStop()
run = false
end |
"Бригадир" смотрит что в нем сверху вниз, видит run = true, записывает себе в журнал, что в таком-то скрипте есть переменная run и она равна true, смотрит дальше, есть функция main, значит нужно будет привлечь другого работника, смотрит дальше, и видит функцию обратного вызова OnTrade, а в должностной инструкции бригадира написано, что это его обязанность обрабатывать функции обратного вызова, он у себя помечает в журнале, что когда придет информация о сделке пользователя, он должен выполнить эту функцию в этом скрипте, смотрит дальше, и видит что есть, так же, функция обратного вызова OnStop, опять помечает у себя в журнале, что если пользователь нажмет кнопку "Остановить" выделив перед этим данный скрипт в окне "Доступные скрипты", то он должен выполнить эту функцию . После этого он, как обычно, подзывает работника, говорит ему выполнять main и продолжает заниматься своей работой по обслуживанию терминала. Работник начинает выполнять то, что в main, смотрит, а в ней цикл "ПОКА(while) переменная run равна true ДЕЛАТЬ(do) ", проверяет переменную run, а она действительно равна true, и начинает делать то, что внутри цикла написано, видит там паузу 10 миллисекунд, ничего не делает 10 мс, идет дальше, видит там end, значит конец цикла, но так как это цикл, то он возвращается обратно к while и снова проверяет значение переменной run, оно по прежнему true и он опять выполняет паузу, и будет продолжать так делать, пока(while) переменная run равна true.
А в это время в терминал приходит информация, что по выставленной пользователем ранее заявке совершилась сделка, бригадир, получив эту информацию, смотрит свой журнал и видит, что при таком событии он должен выполнить функцию OnTrade в нашем скрипте, что он и делает, видит, что он должен вывести сообщение с текстом "Появилась новая сделка", делает это и продолжает заниматься своей работой. Сделок может быть много и каждый раз он будет выводить сообщение.
Потом вдруг нам надоело получать эти сообщения о сделках и мы нажали кнопку "Остановить" для этого скрипта. Бригадир опять читает свой журнал и видит, что должен выполнить функцию OnStop прежде, чем завершить наш скрипт. Переходит к ее выполнению, смотрит, а там написано, что он должен переменной run присвоить значение false, что он и делает, а так же ставит себе таймер на 5 секунд, после срабатывания которого он должен работнику, который выполняет main дать приказ завершить все работы и исчезнуть куда-нибудь, если он сам не придет в течении этих 5 секунд и не скажет, что он все выполнил 🙂
А в это время работник, в очередной раз проверяя значение переменной run, обнаруживает, что она уже не равна больше true, а равна false, а значит не нужно больше выполнять цикл, он переходит к тому, что ниже цикла, видит, что должен вывести сообщение с текстом "Пока", делает это, смотрит дальше, а дальше конец функции main, а значит и конец его работы. Радостный он бежит к бригадиру, сообщает, что все сделал, бригадир отпускает его с работы домой, а у себя из журнала вычеркивает переменную run и функции OnTrade и OnStop, после чего продолжает заниматься своей работой, как будто этого скрипта никогда и не было 🙂
Привет, а в 8 квике функция main автоматически запускается в отдельном потоке?
в 7 квике не пробовал, в 8 запустил вот такой скрипт, и квик насмерть завис
Квик запустит main сам, вот как надо:
результаты выполнения можно посмотреть в бесплатной программке DebugView
Всем привет! Помогите, кто может!? Хочу поставить условие для округления на количество знаков после запятой в зависимости от шага цены инструмента. А при условии скрипт выдаёт пустую таблицу и останавливается.
Добрый вечер! Подскажите, из-за чего нет данных?
Поздравляю, вы изобрели отличный способ убить QUIK. Нельзя бесконечно ожидать чего-то в OnInit() потому, что для того, чтобы выше ожидание закончилось и DataSource начал получать данные, квику нужно выйти из OnInit() и продолжить свое собственное исполнение. Вообще, подобные конструкции не пишите НИКОГДА:
лучше организовать ваш скрипт следующим образом:
Спасибо огромное! А то это мои первые попытки. Индикаторы уже надоели))!
учитесь разбираться до конца, а не бросать начатое. без этого ничего не получится.
В итоге я сделал все индикаторы, которые хотел! Потестировал их! У меня их набралось уже несколько десятков. Но в итоге никакой устойчивой закономерности не нашёл. Либо, если учесть все возможные условия - то КВИК умрёт!))) А при смене графика инструмента нужно будет ждать следующей торговой сессии. Теперь хочу сделать скрипт в виде таблички для расчёта волатильности для нужных инструментов, чтобы не открывать кучу графиков для индикаторов.
это лишь подтверждает мою теорию. 🙂 если у вас "квик умирает", значит это вы что-то делаете неправильно, а не квик плохой. когда вы начнете это понимать, тогда, можно сказать, что самый начальный этап обучения прошел.
Скорее, сначала умру я, а квик будет жить)))! Теперь вот с Вашей помощью, за что очень благодарен, научился разделять процессы обработки свечей в индикаторах. Дельная вещь. При правильном подходе здорово работает.
Сейчас вот сижу и голову ломаю. Давно хочу сделать High-Low уровни сегодня за вчера (то есть от начала сессии вчера до конца сессии вчера) без учёта сегодня. Сегодняшние нахожу так:
расчет high/low по дням
вы получаете таблицу minmax_by_day примерно такого содержимого
дальше можете получать minmax за день вызывая что-то вроде: x = minmax_by_day[20200203].low; вот только вычислять предыдущий день из формата 20200203 не очень удобно, лучше переводить T(indx) в unixtime. как работать с unixtime погуглите в интернете.
вот это интересный способ! Только я вот не до конца понимаю (current_date = dt.year * 10000 + dt.month * 100 + dt.day). Или это какой-то максимальный параметр? Почему год на 10000, месяц на 100, а день без умножения? Это получается как захват диапазона данных? А по скорости? что быстрее? перебор значений или math.max ?
А вот minmax_by_day[current_date] = minmax - это переход на следующий день?
я прошу прощения.

Изображение:
Добрый день! С unixtime не нашёл способа применить, поэтому сделал так:
unixtime - это число секунд, прошедшее с 1 января 1970 года. можно преобразовать таблицу, которую возвращают функции квика в этот формат.
зачем? затем, что с временем в секундах очень удобно работать. просто сравнивая целые числа можно легко понять, одна дата меньше другой или нет. если вычесть эти числа - то понятно насколько (в секундах).
так же довольно удобно вычислять моменты в прошлом и будущем. для этого нужно освоить две математические операции: деление нацело и получение остатка от деления.
если из текущего времени в unixtime отнять остаток от деления его на число x = 24*60*60, то мы получим 0 часов 0 минут текущего дня. прибавив 18 * 60 *60 + 45*60, мы получим значение 18:45 текущего дня - начало клиринга, с которым можно сравнивать текущее время, например:
если нужно просто сравнивать даты, то можно легко получить кол-во дней: day = unix_dt % (24*60*60), тогда "вчера" это day - 1, за "завтра" это days+1.
обратная функция для перевода unixtime в формат lua: os.date(). применять примерно так: os.date("*t", unix_time)
опечатался. day = math.floor(unix_dt / (24*60*60)). для вычисления остатка вместо оператора % так же можно использовать math.fmod(unix_dt, (24*60*60))
Добрый день! А если сразу использовать cur_dt = os.date("*t")? Тут получаем таблицу.
После, например, для вчерашнего дня yd = cur_dt.day - 1.
Чем лучше переводить в секунды, а потом группировать их обратно в минуты, часы, дни и т.д?
И вот ещё вопрос: os.time возвращает время компьютера. А если часовой пояс другой? Тогда и значения часов будут некорректными.?
тем, что для 1 числа месяца cur_dt.day - 1 будет равен нулю, что является ошибкой. нам нужно проверить, сколько дней в предыдущем месяце: 28, 29, 30 или 31 и и в этом случае присвоить соответствующее значение, уменьшить на единицу dt.month, уменьшить на единицу dt.year, если это было 1 января и присвоить month 12. учесть високосный год и все такое прочее.
если часовой пояс другой, то обычно принято все приводить к UTC.
ps: вся вот эта ваша хренота вроде "if dt.month == month_server -1" не будет нормально работать в январе.
Сколько тонкостей! Я-то планировал время, как свечи перебирать (годы, месяцы, часы, минуты). Наверно, суть в том, что в UNIX учитывается только реально прошедшее время, а не гипотетическое?!
Здравствуйте. Подскажите какое значение ставить в функцию sleep, чтобы не сильно загружать процессор?
Здравствуйте,
смотря какой расчет вы делаете за одну итерацию. 1 мс, обычно достаточно.
Включите диспетчер задач и посмотрите нагрузку ЦП при включенном роботе и выключенном, при свернутом окне терминала.
Если нагрузка бота на ЦП вас не устраивает, то оптимизируйте код или добавьте дополнительный sleep.
Спасибо
Дмитрий, добрый день
Столкнулся вот с какой проблемкой:
запускаю скрипт, содержащий функцию остановки, после того как скрипт запущен, функция работает отлично, нажимаю стоп-все останавливается , далее снова включается и после нажатия стоп снова останавливает выполнение скрипта. Но стоит скрипту поработать какое то время, пройти несколько циклов и после нажатия на стоп-все зависает, скрипт выключается только после закрытия самого Quik через диспетчер задач.
Подскажите пожалуйста в чем может быть проблема. Спасибо
Здравствуйте, Андрей! С Новым Годом! 🙂 Это где-то в скрипте у Вас логическая ошибка, скорее всего где-то цикл while, который не смотрит на значение флага типа IsRun, который должен сбрасываться в OnStop.
Можно ли запустить один скрипт, а из него запустить другие необходимые скрипты?
При аварийных перезапусках Quik как автоматически запускать скрипты или один скрипт, который запустит остальные? Какие существуют методики контроля работы Quik и аварийных перезапусков его?
При нормальном выключении квик запоминает втом числе и работающие скрипты. При запуске они все их запустит еще до авторизации.
Спасибо. Да, когда закрываешь Quik без закрытия скриптов, они запускаются при запуске Quik.
gostardo не верю, что нельзя обойтись без OnInit()! GetCurrentOffersAndBids() в студию!
Как перевести данные из потока Квика в function main()? Мне казалось, что любые глобальные переменные, если они меняются в функции обратного вызова должны быть видны в function main() уже измененными. Однако, в простейшем примере:
переменная izm при изменении в таблице параметров должна поменяться с izm = false на izm = true, однако она все время остается false.
Я, по-видимому, в чем-то ошибаюсь?
Но ведь переменная IsRun меняется в function OnStop().
У Вас строка 18 только 1 раз при запуске выполняется, вот так нужно:
Большое спасибо!
Ошибка-то элементарная, но в этом направлении даже не смотрел, думал не знаю каких-то основ в передаче данных в function main(), а найти их сам в интернете не смог. Вообще, почему-то, народ пишет роботов в основном, не пользуясь function main() и function OnInit(), хотя, насколько я понимаю, это достаточно важно для скорости исполнения.
Всегда пожалуйста! Я вообще плохо представляю как можно написать робота без main 🙂 В конце этой статьи добавил маленькую сказочку по этому поводу 🙂 Прочтите, пожалуйста, надеюсь, Вам станут понятнее многие вещи.
для чего именно нужна функция function OnInit() ?
если я тоже самое пропишу просто перед main, не в какой либо функции, типо t=0, dt1=os.date("*t") и т.д. чем это отличается от прописанного в OnInit()? можно пример?
В функцию OnInit передается полный путь к скрипту (когда-то это бывает нужно), еще Вы не сможете, например, использовать функцию CreateDataSource в самом теле скрипта, еще какие-то функции в самом теле скрипта не будут работать, но я не знаю весь список. Тут, вообще, нужно проще рассуждать, если Вам хватает того функционала, который у Вас есть, то и не усложняйте себе жизнь 🙂 Лично я практически никогда не использую данную функцию.
Здравствуйте, Дмитрий. Не правда ваша, CreateDataSource вызывается нормально и без OnInit, как в прочем и остальные функции, перечисленные в руководстве.
Здравствуйте! Запускаю сейчас вот такой скрипт на демке:
и вижу сообщение "Ошибка подключения к графику: invalid context"
Конечно будет ошибка, Вы пытаетесь нагрузить основной поток, вот так будет работать:
Александр, а Вы внимательно прочитали мой комментарий, в ответ на который Вы написали, что я ошибаюсь? 🙂 Там и написано про основное тело скрипта, а не внутренности main.
Да внимательно, и речь шла о функции OnInit, а тело скрипта, по Вашему мнению - это основной поток. А какой частью "тела" является main? - нога?)))
Основное тело скрипта это промежутки между функциями, внутри функции уже тело функции, прочитайте тогда еще вопрос, на который я отвечал тем комментарием 🙂
Там есть фраза"не в какой либо функции" 🙂
Не будем спорить, факт в том, что функция OnInit бесполезна, тому пример - примеры из руководства QLUA, где она не используется.
Да, я, как раз, и писал в том комментарии, что я ее практически не использую, так что согласен!
Только что столкнулся с ситуацией, где OnInit() помогла.
Мне нужно перед началом работы скрипта вызвать функцию GetCurrentOffersAndBids(), использующей функцию getQuoteLevel2.
Если я сделаю это просто перед main(), то получу ошибку "attempt to call global 'GetCurrentOffersAndBids' (a nil value)". Помещение вызова GetCurrentOffersAndBids() внутрь OnInit() решило проблему.
Если я правильно понимаю, то sleep работает корректно только в main'е. Чтобы иметь возможность приостановить работу скрипта из любой точки, я написал свой собственный sleep. Вдруг пригодится кому-то.
Здравствуйте!
Функция sleep везде работает корректно, она ставит на паузу тот поток, в котором она выполняется. Функция main - один поток (отдельный от основного), все функции, которые будут вызваны из функции main так же будут работать вне основного потока.
Все остальное - другой поток (основной поток терминала).
Отличием функции sleep от того, что предлагаете Вы, является то, что при вызове sleep процессор перестает обрабатывать поток, в котором вызвана эта функция, а в Вашем варианте Вы наоборот этот поток нагружаете. Чтобы понять о чем я говорю, запустите данный скрипт:
А теперь откройте "Диспетчер задач" Windows и посмотрите на сколько процентов QUIK загружает процессор, теперь остановите скрипт и посмотрите какое время будет у выведенных сообщений, а теперь раскомментируйте строку 5 и закомментируйте строку 6, запустите скрипт и посмотрите на сколько теперь процентов QUIK загружает процессор.
Вообще, не стоит делать паузу в основном потоке, т.е. где-то кроме main, чтобы понять почему не стоит это делать, установите в строке 12 в функцию sleep значение 5000, запустите скрипт, потом остановите его и попробуйте в эти 5 секунд, с момента нажатия на кнопку "Остановить" подвигать график, например, Вы увидите, что терминал не реагирует на Ваши действия, потому что Вы приостановили основной поток выполнения терминала.
Насчёт нагрузки на ЦП согласен. Действительно, возрастает многократно.
Не понял насчёт потоков - можно ещё раз? Сам терминал выполняется в одном потоке, main - в другом. А что насчёт функций, вызванных из main? Вообще, все прочие функции в конце-концов вызываются через main. И они, насколько я понял, выполняются в том же потоке, что и сам терминал.
Как-то было дело - писал скрипт, и почему-то ваш sleep не отрабатывал в вызванной функции. Поэтому написал свой. Сейчас погонял оба варианта - разницы не увидел, кроме нагрузки на процессор. Видимо, тогда я в чём-то ошибся.
В общем, согласен, мой вариант лучше не использовать.
Сам терминал и все функции обратного вызова скриптов работают в одном потоке, для того, чтобы в скрипте можно было делать какие-то ресурсоемкие вычисления и терминал при этом не "висел", разработчики сделали так, что для функции main создается отдельный поток, который никак не тормозит основной поток терминала. Чтобы объяснить на счет функций вызываемых из main, приведу тот же пример:
Если Вы его запустите, то увидите в диспетчере задач, что использование процессора выросло, т.к. в main используется Ваш Sleep, но терминал при этом реагирует на действия пользователя, потому что Sleep выполняется в отдельном от терминала потоке (вызывается в main). Но когда Вы остановите скрипт, то Sleep будет вызвана не в отдельном потоке main, а а в основном, как я писал выше, все функции обратного вызова выполняются в основном потоке и OnStop не исключение. Важно понимать следующее, функция - это инструкция записанная в память, сама по себе она ничего не делает. Когда в терминале запускается скрипт, то терминал находит в скрипте функцию main, он знает, что для main нужно создавать отдельный поток, он его создает и начинает в нем выполнять все инструкции написанные в main, т.е. для каждого скрипта есть свой отдельный поток main. Так же, при инициализации скрипта терминал смотрит какие On-функции есть в скрипте и запоминает их, функции обратного вызова это ничто иное, как подписки на события происходящие в терминала.
И вот работает терминал в своем потоке, работают параллельно потоки main запущенных скриптов, вдруг в терминале происходит какое-то событие, как в нашем примере, пользователь нажал "Остановить", терминал знает, что скрипты могут подписываться на это событие при помощи функции OnStop, терминал знает уже, что останавливаемый скрипт подписался на это событие и выполняет инструкции написанные в функции OnStop, и выполняет он их в своем потоке, а поток main так и продолжает работать параллельно, ничего не подозревая. Но во время выполнения функции OnStop было изменено значение глобальной переменной run, цикл while остановился, функция main выполнила все инструкции в ней прописанные, потоку main больше нечего стало делать, он завершился, а значит завершилось выполнение скрипта, потому что скрипт это и есть поток main.
Стало понятнее?
Дмитрий, большое спасибо за подробный ответ. Всё стало очень понятно!
Всегда пожалуйста!
Здравствуйте. Подскажите какое значение ставить в функцию sleep, что не сильно загружать процессор?
Здравствуйте. 10 по моему мнению самое то
спасибо