База скрипта в QLua (lua)

Автор записи: Дмитрий (Admin)
1 звезда2 звезды3 звезды4 звезды5 звезд (Голосов 5, среднее: 5,00 из 5)
Загрузка...

-- Это основная функция скрипта, которая работает в отдельном от QUIK потоке,
-- это означает, что если QUIK будет перегружен работой,
-- например, при большом объеме поступающих сделок,
-- то QLua - скрипт продолжит выполнять свою работу в штатном режиме,
-- при условии, что у QUIK найдется процессорное время для того, чтобы "выдать" скрипту
-- необходимую информацию для работы (если она ему потребуется)
function main()
   -- здесь будет Ваш код
end;
-- Если запустить такой скрипт, то эта функция выполнится 1 раз и скрипт остановится.

-- Если Вам нужно постоянно выполнять какой-то код,
-- то для этого нужно внутри этой функции расположить т.н. "бесконечный цикл",
-- но для того, чтобы возможность остановить скрипт все-таки была,
-- делается подобным образом:
 
IsRun = true;
 
function main()
   while IsRun do
 
   -- здесь будет Ваш код
 
   sleep(1); 
   -- ОБЯЗАТЕЛЬНО !!!(это пауза в 1 миллисекунду), ИНАЧЕ СКРИПТ БУДЕТ СИЛЬНО НАГРУЖАТЬ ПРОЦЕССОР
   -- И ПРИ ЗАВЕРШЕНИИ ТАКОГО СКРИПТА QUIK МОЖЕТ АВАРИЙНО ЗАВЕРШИТЬ СВОЮ РАБОТУ
   end;
end;
 
function OnStop()
   IsRun = false;
end;
 
-- Функция OnStop вызывается автоматически, когда пользователь нажимает кнопку "Остановить",
-- или закрывает терминал.
-- Внутри этой функции можно расположить какой-то свой код,
-- который нужно выполнить по завершению работы скрипта.
-- В данном случае при завершении скрипта переменной IsRun присваивается ЛОЖЬ,
-- после чего цикл while внутри функции main перестает выполнятся и скрипт останавливается.
 
-- ВАЖНО!!! Не помещайте в эту функцию код, которому нужно много времени для выполнения,
-- потому что, время выполнения этой функции ограничено примерно 5-ю секундами.
-- Так же, как OnStop, существует функция инициализации Init, 
-- которая, в отличие от OnStop, выполняется до запуска скрипта,
-- перед началом работы функции main
 
IsRun = true;
 
function OnInit()
   -- Здесь будет Ваш код для начальной инициализации
end;
 
function main()
   while IsRun do
 
   -- Здесь будет Ваш код,
   -- который будет выполнятся,
   -- пока скрипт не остановлен
 
   sleep(1);
   end;
end;
 
function OnStop()
   -- Здесь будет Ваш код,
   -- который нужно выполнить
   -- перед остановкой скрипта
   IsRun = false;
end;
Сказочка про работу скрипта 🙂
Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!

Добавить комментарий

База скрипта в QLua (lua): 26 комментариев

  1. Можно ли запустить один скрипт, а из него запустить другие необходимые скрипты?
    При аварийных перезапусках Quik как автоматически запускать скрипты или один скрипт, который запустит остальные? Какие существуют методики контроля работы Quik и аварийных перезапусков его?

  2. Как перевести данные из потока Квика в function main()? Мне казалось, что любые глобальные переменные, если они меняются в функции обратного вызова должны быть видны в function main() уже измененными. Однако, в простейшем примере:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    function OnInit()
    IsRun = true
    time = os.clock()
    izm = false
    tbid = 0
    tbidvol = 0
    end
    -- проверка изменений в таблице
    function OnParam(class, sec)
       if (class =="SPBFUT" and  sec == "SiH7") then
          tbid = tonumber(getParamEx(class, sec, "bid").param_value)
          tbidvol = tonumber(getParamEx(class, sec, "BIDDEPTH").param_value)
    		 izm = true
       end
    end
     
    function main()
    	if izm then message("true",1) else message("false",1)
           end
       while IsRun do
          sleep(100) 
       end
    end
     
    function OnStop()
       IsRun = false
    end

    переменная izm при изменении в таблице параметров должна поменяться с izm = false на izm = true, однако она все время остается false.
    Я, по-видимому, в чем-то ошибаюсь?
    Но ведь переменная IsRun меняется в function OnStop().

      1. Большое спасибо!
        Ошибка-то элементарная, но в этом направлении даже не смотрел, думал не знаю каких-то основ в передаче данных в function main(), а найти их сам в интернете не смог. Вообще, почему-то, народ пишет роботов в основном, не пользуясь function main() и function OnInit(), хотя, насколько я понимаю, это достаточно важно для скорости исполнения.

        1. Всегда пожалуйста! Я вообще плохо представляю как можно написать робота без main 🙂 В конце этой статьи добавил маленькую сказочку по этому поводу 🙂 Прочтите, пожалуйста, надеюсь, Вам станут понятнее многие вещи.

  3. для чего именно нужна функция function OnInit() ?
    если я тоже самое пропишу просто перед main, не в какой либо функции, типо t=0, dt1=os.date("*t") и т.д. чем это отличается от прописанного в OnInit()? можно пример?

    1. В функцию OnInit передается полный путь к скрипту (когда-то это бывает нужно), еще Вы не сможете, например, использовать функцию CreateDataSource в самом теле скрипта, еще какие-то функции в самом теле скрипта не будут работать, но я не знаю весь список. Тут, вообще, нужно проще рассуждать, если Вам хватает того функционала, который у Вас есть, то и не усложняйте себе жизнь 🙂 Лично я практически никогда не использую данную функцию.

      1. Здравствуйте, Дмитрий. Не правда ваша, CreateDataSource вызывается нормально и без OnInit, как в прочем и остальные функции, перечисленные в руководстве.

        1. Здравствуйте! Запускаю сейчас вот такой скрипт на демке:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          
          ds, Error = CreateDataSource("QJSIM", "SBER", INTERVAL_M1);
           
          Run = true
           
          function main()
             while (Error == "" or Error == nil) and ds:Size() == 0 do sleep(1) end
             if Error ~= "" and Error ~= nil then message("Ошибка подключения к графику: "..Error) end
             while Run do
                sleep(10)
             end   
          end
           
          function OnStop()
             Run = false
          end

          и вижу сообщение "Ошибка подключения к графику: invalid context"

          1. Конечно будет ошибка, Вы пытаетесь нагрузить основной поток, вот так будет работать:

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            
            Run = true
             
            function main()
              ds, Error = CreateDataSource("QJSIM", "SBER", INTERVAL_M1);
             while (Error == "" or Error == nil) and ds:Size() == 0 do sleep(1) end
               if Error ~= "" and Error ~= nil then message("Ошибка подключения к графику: "..Error) end
               while Run do
                  sleep(10)
               end   
            end
             
            function OnStop()
               Run = false
            end
              1. Да внимательно, и речь шла о функции OnInit, а тело скрипта, по Вашему мнению - это основной поток. А какой частью "тела" является main? - нога?)))

                    1. Только что столкнулся с ситуацией, где OnInit() помогла.
                      Мне нужно перед началом работы скрипта вызвать функцию GetCurrentOffersAndBids(), использующей функцию getQuoteLevel2.

                      Если я сделаю это просто перед main(), то получу ошибку "attempt to call global 'GetCurrentOffersAndBids' (a nil value)". Помещение вызова GetCurrentOffersAndBids() внутрь OnInit() решило проблему.

  4. Если я правильно понимаю, то sleep работает корректно только в main'е. Чтобы иметь возможность приостановить работу скрипта из любой точки, я написал свой собственный sleep. Вдруг пригодится кому-то.

    1
    2
    3
    4
    5
    
    -- принимает время в секундах
    function Sleep(S)
      local ntime = os.time() + S;
      repeat until os.time() > ntime;
    end;
    1. Здравствуйте!
      Функция sleep везде работает корректно, она ставит на паузу тот поток, в котором она выполняется. Функция main - один поток (отдельный от основного), все функции, которые будут вызваны из функции main так же будут работать вне основного потока.
      Все остальное - другой поток (основной поток терминала).
      Отличием функции sleep от того, что предлагаете Вы, является то, что при вызове sleep процессор перестает обрабатывать поток, в котором вызвана эта функция, а в Вашем варианте Вы наоборот этот поток нагружаете. Чтобы понять о чем я говорю, запустите данный скрипт:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
      run = true
       
      function main()
         while run do
            --sleep(1000)
            Sleep(1)
         end
      end
       
      function OnStop()
         message('До sleep')
         sleep(1000)
         message('После sleep')
         run = false
      end
       
      function Sleep(S)
        local ntime = os.time() + S;
        repeat until os.time() > ntime;
      end

      А теперь откройте "Диспетчер задач" Windows и посмотрите на сколько процентов QUIK загружает процессор, теперь остановите скрипт и посмотрите какое время будет у выведенных сообщений, а теперь раскомментируйте строку 5 и закомментируйте строку 6, запустите скрипт и посмотрите на сколько теперь процентов QUIK загружает процессор.
      Вообще, не стоит делать паузу в основном потоке, т.е. где-то кроме main, чтобы понять почему не стоит это делать, установите в строке 12 в функцию sleep значение 5000, запустите скрипт, потом остановите его и попробуйте в эти 5 секунд, с момента нажатия на кнопку "Остановить" подвигать график, например, Вы увидите, что терминал не реагирует на Ваши действия, потому что Вы приостановили основной поток выполнения терминала.

      1. Насчёт нагрузки на ЦП согласен. Действительно, возрастает многократно.
        Не понял насчёт потоков - можно ещё раз? Сам терминал выполняется в одном потоке, main - в другом. А что насчёт функций, вызванных из main? Вообще, все прочие функции в конце-концов вызываются через main. И они, насколько я понял, выполняются в том же потоке, что и сам терминал.
        Как-то было дело - писал скрипт, и почему-то ваш sleep не отрабатывал в вызванной функции. Поэтому написал свой. Сейчас погонял оба варианта - разницы не увидел, кроме нагрузки на процессор. Видимо, тогда я в чём-то ошибся.
        В общем, согласен, мой вариант лучше не использовать.

        1. Сам терминал и все функции обратного вызова скриптов работают в одном потоке, для того, чтобы в скрипте можно было делать какие-то ресурсоемкие вычисления и терминал при этом не "висел", разработчики сделали так, что для функции main создается отдельный поток, который никак не тормозит основной поток терминала. Чтобы объяснить на счет функций вызываемых из main, приведу тот же пример:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          
          run = true
           
          function main()
             while run do
                Sleep(5)
             end
          end
           
          function OnStop()
             Sleep(5)
             run = false
          end
           
          function Sleep(S)
            local ntime = os.time() + S;
            repeat until os.time() > ntime;
          end

          Если Вы его запустите, то увидите в диспетчере задач, что использование процессора выросло, т.к. в main используется Ваш Sleep, но терминал при этом реагирует на действия пользователя, потому что Sleep выполняется в отдельном от терминала потоке (вызывается в main). Но когда Вы остановите скрипт, то Sleep будет вызвана не в отдельном потоке main, а а в основном, как я писал выше, все функции обратного вызова выполняются в основном потоке и OnStop не исключение. Важно понимать следующее, функция - это инструкция записанная в память, сама по себе она ничего не делает. Когда в терминале запускается скрипт, то терминал находит в скрипте функцию main, он знает, что для main нужно создавать отдельный поток, он его создает и начинает в нем выполнять все инструкции написанные в main, т.е. для каждого скрипта есть свой отдельный поток main. Так же, при инициализации скрипта терминал смотрит какие On-функции есть в скрипте и запоминает их, функции обратного вызова это ничто иное, как подписки на события происходящие в терминала.
          И вот работает терминал в своем потоке, работают параллельно потоки main запущенных скриптов, вдруг в терминале происходит какое-то событие, как в нашем примере, пользователь нажал "Остановить", терминал знает, что скрипты могут подписываться на это событие при помощи функции OnStop, терминал знает уже, что останавливаемый скрипт подписался на это событие и выполняет инструкции написанные в функции OnStop, и выполняет он их в своем потоке, а поток main так и продолжает работать параллельно, ничего не подозревая. Но во время выполнения функции OnStop было изменено значение глобальной переменной run, цикл while остановился, функция main выполнила все инструкции в ней прописанные, потоку main больше нечего стало делать, он завершился, а значит завершилось выполнение скрипта, потому что скрипт это и есть поток main.
          Стало понятнее?