Получение стакана из QUIK в QLua(Lua)

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

QUIK-Qlua-poluchenie-dannyh
Для получения стакана в QLua(Lua) служит функция обратного вызова OnQuote(). Эта функция вызывается терминалом QUIK при получении изменения стакана котировок. Для получения данных самого стакана служит функция getQuoteLevel2().

В терминале должен быть открыт стакан по нужному инструменту!!!

 

Пример:

 

--- Функция вызывается терминалом QUIK при получении изменения стакана котировок
function OnQuote(class, sec )
   -- 
   if class == "SPBFUT" and sec == "RIM5" then
      ql2 = getQuoteLevel2(class, sec);
      -- Представляет снимок СТАКАНА в виде СТРОКИ
         QuoteStr = "";
         for i = tonumber(ql2.bid_count), 1, -1 do
            if ql2.bid[i].quantity ~= nil then   -- На некоторых ценах могут отсутствовать заявки
               QuoteStr = QuoteStr..tostring(tonumber(ql2.bid[i].quantity))..";"..tostring(tonumber(ql2.bid[i].price))..";";
            else
               QuoteStr = QuoteStr.."0;"..tostring(tonumber(ql2.bid[i].price))..";";
            end;
         end;
         for i = 1, tonumber(ql2.offer_count), 1 do
            if ql2.offer[i].quantity ~= nil then   -- На некоторых ценах могут отсутствовать заявки
               if i < tonumber(ql2.offer_count) then 
                  QuoteStr = QuoteStr..tostring(tonumber(ql2.offer[i].quantity))..";"..tostring(tonumber(ql2.offer[i].price))..";";
               else 
                  QuoteStr = QuoteStr..tostring(tonumber(ql2.offer[i].quantity))..";"..tostring(tonumber(ql2.offer[i].price));
               end;
            else 
               if i < tonumber(ql2.offer_count) then
                  QuoteStr = QuoteStr.."0;"..tostring(tonumber(ql2.offer[i].price))..";";
               else
                  QuoteStr = QuoteStr.."0;"..tostring(tonumber(ql2.offer[i].price));
               end;
            end;
         end;
   end;
end;
 
-- В результате при каждом обновлении стакана по инструменту RTS-6.15 в переменной QuoteStr будет строка, типа:
   -- "15;86130;17;86120;16;86110;22;86100;16;86090;24;86080;26;86070;29;86060;97;86050;51;86040;99;86030;88;86020;143;86010;140;86000;15;85990;49;85980;58;85970;28;85960;49;85950;36;85940;71;85930;115;85920;95;85910;25;85900;5;85890;13;85880;62;85870;3;85860;36;85850;26;85840;11;85830;9;85820;99;85810;86;85800;41;85790;24;85780;2;85770;36;85760;162;85750;22;85740;44;85730;76;85720;229;85710;121;85700;5;85690;5;85680;2;85670;10;85660;148;85650;119;85640;12;86150;19;86160;26;86170;32;86180;71;86190;49;86200;26;86210;20;86220;91;86230;57;86240;47;86250;25;86260;34;86270;41;86280;21;86290;66;86300;36;86310;57;86320;50;86330;34;86340;38;86350;22;86360;17;86370;15;86380;18;86390;13;86400;3;86410;34;86420;29;86430;80;86440;26;86450;68;86460;226;86470;371;86480;30;86490;64;86500;99;86510;11;86520;2;86530;23;86540;9;86550;3;86560;52;86570;23;86580;25;86590;73;86600;17;86610;88;86620;52;86630;150;86640"
   -- Такую строку удобно, в последствии, передавать в C# и разделять на элементы
 
-- Функция getQuoteLevel2() принимает 2 параметра: Код класса и Код бумаги, а возвращает таблицу, которая имеет следующие поля:
bid_count    -- Количество котировок покупки (STRING)
offer_count  -- Количество котировок продажи (STRING)
bid          -- Котировки спроса (покупки) (TABLE)
offer        -- Котировки предложений (продажи) (TABLE)
   -- Таблицы «bid» и «offer» имеют следующую структуру: 
      price     -- Цена покупки / продажи (STRING)
      quantity  -- Количество в лотах (STRING)

Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!

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

Получение стакана из QUIK в QLua(Lua): 145 комментариев

  1. Подскажите, пожалуйста, а можно ли экспортировать параметры стакана, в частности, например, настроить графическое отображение и экспорт статистики спреда bid-offer, который отображается в шапке стакана цен?

    Изображение:

      1. На данный момент получилось так:
        Изображение:

        Как видно, внутри дня считается, сохраняется (подкачивается) и история изменения. Осталось построить спред между двумя показателями. Как это можно сделать?

        1. У вас получилась, извиняюсь за выражение - хрень.
          Не известно какие срезы цен бид и аск внутри минуток.
          Т.е. ничего не видно.
          Ни один брокер, ни тем паче биржа, историю стаканов не хранит, и тем более не транслирует.
          Нижние кривые это случайные величины, взятые терминалом, на основании цен текущего таймфрейма графика.
          Так что ручками пишем робота, который будет хранить историю спредов.
          Ни для кого не секрет, что сия информация вам ничего не даст, абсолютно.
          Зря потратите время.

            1. Бесспорно строится, на основании тиков будет точнее, но скорее всего терминал задохнется, если длинна тикового ряда будет более 500 тиков, т.е. в моменты повышенной волатильности минуты две.
              Стратегия это управление рисками, а все остальные "стратегии" лишь сценарии с паттернами, то бишь с сигналами.

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

                В принципе, в квике это выполняется (показатели объема есть, спред в стакане тоже отображается), но хотелось бы немного углубить задачу, имея возможность обрабатывать эти данные и анализировать статистику.
                Буду благодарен, если подскажите как вывести эти данные во внешнюю среду.

                1. Подытожим.
                  То, что вы видите графически в квике и то, что на самом деле происходит в момент времени - две разные вещи.
                  Данные по стакану приходят снепшотами по порядку, но никому не известен интервал снепшотов.
                  Это значит, что вы в стакане будете видеть ВСЕГДА! спред в 0,01%, а на деле между снепшотами были спреды 1-50+++%. Которые очень сильно испортят вам картинку статистики и не дай бог счета.
                  И что вы понимаете под "внешней средой"? Уборная в огороде тоже в какой-то мере внешняя среда.
                  Напишите макрос в эксель, который будет реагировать на изменения ячеек бид/аск в стакане, подсчитывать спред и выводить его на отдельный лист и сохранять в файл. И стройте себе графики, и анализируйте что вам нужно.
                  Как вывести стакан по ДДЕ есть в файле-справке квика.

  2. Здравствуйте. А разве функция OnQuote() не должна получать ВСЕ изменения во ВСЕХ стаканах терминала? Или я что-то не так понимаю?
    Дело в том, что внутри функции отслеживаю изменения стаканов срочного рынка(стоит условие по классу). Так кроме фьючерса на индекс РТС ничего не приходит. Я уже ввел условие, чтоб выводился в message тикер бумаги(sec из примера кода) только тогда, когда он не равен "RIZ0", но ни одного сообщения так и не получил.
    У меня есть подозрение, что это что-то вроде "rate condition". Только здесь информация об изменениях в других стаканах не успевает вклиниться между очень частыми изменениями по "RIZ0". Только непонятно почему изменения не становятся в очередь на отправку в функцию OnQuote(), а всегда пропускают "RIZ0" вперед.

    1. Здравствуйте, подозреваю, что у вас открыт один стакан, если это так, то откройте нужные стаканы
      или можете в скрипте заказать события по другим инструментам функцией:
      Subscribe_Level_II_Quotes(class_code, sec_code), посмотрите в QLUA.chm.
      Либо у вас очень много кода в OnQuote.
      События не накапливаются, если OnQuote занят расчетами, то новые события отбрасываются.
      Но это мало вероятно, что каждое новое событие, которое попадает в функцию - строго РТС, а остальные были отброшены (вероятность есть, но маленькая).

  3. Дмитрий (Admin), приветствую! Не могли бы Вы не немного пояснить про взаимодействие Subscribe_Level_II_Quotes с Функциями OnQuote() и getQuoteLevel2()?
    Допустим я заказываю в OnInit() несколько стаканов функцией Subscribe_Level_II_Quotes (и проверяю заказались ли они). В функции OnStop() отписываюсь от них соотвественно.
    Но мне совершенно не нужно получить данные с OnQuote(), там же их куча целая будет каждую секунду... Мне надо периодически (допустим раз в минуту) снимать состояние ранее заказанных стаканов. Если вообще не добавлять в скрипт OnQuote() стаканы же меняться не будут и без этого getQuoteLevel2() ничего нового не посчитает или работать не будет?
    Чтобы ресурсы ПК не отжирать как сделать так, чтобы стаканы смотреть/сохранять допустим раз в минуту? Отписываться и снова подписываться на них раз в минуту?
    Я видимо что-то недопонимаю...

  4. Как-то не заметил, вот такой пример, Дмитрий - будет создавать задержки получения данных терминалом.
    При повышенной волатильности, это может быть заметно визуально.
    Особенно не стоит делать какие-то серьезные вычисления в функциях обратного вызова: OnAllTrade, OnParam, OnQuote.
    Да и в остальных тоже.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    local QuoteThread = {} -- таблица для обмена данными между потоками
    function OnQuote(class, sec )
       if class == "SPBFUT" and sec == "RIM5" then
          table.sinsert(QuoteThread, getQuoteLevel2(class, sec)) 
       end
    end
    function OnQuoteDo( ql2 )
      -- тут обрабатываем стакан ql2
    end
    function main()
      is_run = true
      while is_run do
        while #QuoteThread > 0 do -- обрабатываем все накопленные изменения стакана
    	OnQuoteDo(table.sremove(QuoteThread, 1))
            -- вот тут никаких sleep - ов не нужно, обрабатываем, максимально используя проц.
        end
        sleep(1)
      end
    end
    function OnStop()
      is_run = false
      return 1000
    end

    По аналогии обрабатываются остальные функции обратного вызова.

    1. kalikazandr, здравствуйте! А как быть в таком варианте исполнения, когда необходимо отслеживать сразу два актива?

      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
      
       local QuoteTable1 = {} -- таблица для обмена данными между потоками
      local QuoteTable2 = {}
      function OnQuote(class_code, sec_code )
         if class == "SPBFUT" and sec == "SRU9" then
            table.sinsert(QuoteTable1, getQuoteLevel2(class_code, sec_code)) 
         end
         if class == "SPBFUT" and sec == "SRZ9" then
            table.sinsert(QuoteTable2, getQuoteLevel2(class_code, sec_code)) 
         end
      end
      function OnQuoteDo( ql21, ql22 )
        -- тут обрабатываем стакан ql2
      end
      function main()
        is_run = true
        while is_run do
          while #QuoteTable1 > 0 do <-- КАК БЫТЬ ЗДЕСЬ?
      	OnQuoteDo(table.sremove(QuoteTable1, 1), table.sremove(QuoteTable2, 1))
              -- вот тут никаких sleep - ов не нужно, обрабатываем, максимально используя проц.
          end
          sleep(1)
        end
      end
      function OnStop()
        is_run = false
        return 1000
      end
      1. Здравствуйте.
        Я не автор данного кода, но предложу свои идеи.
        Вариант первый, раз в функции OnQuote разделяете на 2 таблицы, тогда в функции main() обрабатывайте 2 таблицы, сначала 1, потом вторую.
        У Вас первая уже обрабатывается
        while #QuoteTable1 .....
        Добавьте обработку второй таблицы ниже
        while #QuoteTable2 ....
        Хотя я бы вообще не разделял на 2 отдельных таблицы, а все данные отправлял в одну таблицу, а потом в main уже обрабатывал одну таблицу, где разделял бы по бумагам.
        Для этого надо переделать функцию OnQuote

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        
        function OnQuote(class_code, sec_code )
           -- Фильтрую нужные бумаги
           if (class == "SPBFUT" and (sec == "SRU9" or sec == "SRZ9")) then
              -- Получаю данные
              local level2 = getQuoteLevel2(class_code, sec_code);
             -- Так как getQuoteLevel2 не возвращает бумагу - Добавляю поля с информацией о бумаге
              level2.class_code = class_code; level2.sec_code = sec_code;
              -- Добавляю данные в таблицу, которая будет обработана в main
              table.sinsert(QuoteTable1, level2); 
           end
        end

        Ну и соответственно обрабатываю только таблицу QuoteTable1 в main

      2. Данный вариант возможно не будет работать быстрее, или расходовать меньше памяти. Можете протестировать оба варианта и остановиться на том, который покажет лучшие результаты.

      3. Здравствуйте, со стаканом немного не так. Если код не большой, то обработать событие можно сразу в OnOnQuote.
        Или так:
        local QuoteTable = {}
        function OnQuote(class_code, sec_code )
        if class_code == "SPBFUT" and sec_code == "SRU9" then
        table.sinsert(QuoteTable, {getQuoteLevel2(class_code, sec_code), class_code, sec_code})
        end
        if class_code == "SPBFUT" and sec_code == "SRZ9" then
        table.sinsert(QuoteTable, {getQuoteLevel2(class_code, sec_code), class_code, sec_code})
        end
        end
        function OnQuoteDo(tab )
        local ql2 = tab[1]
        local class_code, sec_code = tab[2], tab[3]
        -- тут обрабатываем стакан ql2
        end
        local is_run = true
        function main()
        while is_run do
        while #QuoteTable > 0 and is_run do
        OnQuoteDo(table.sremove(QuoteTable, 1))
        -- вот тут никаких sleep - ов не нужно, обрабатываем, максимально используя проц.
        end
        sleep(1)
        end
        end
        function OnStop()
        is_run = false
        return 1000
        end

        1. kalikazandr правильно говорит - "обработать событие можно сразу в OnQuote" и не нужно отправлять данные в таблицу QuoteTable.
          Если у Вас код обрабатывает данные за 0.005 сек не вижу необходимости выносить обработку в отдельный поток, обрабатывайте в потоке в котором Квик работает, то есть в самой функции OnQuote() и не тратьте ресурсы на запись данных в таблицу QuoteTable, ну подвесите Квик на 0.003 сек и что от этого изменится.
          Другое дело, если у Вас происходят серьезные вычисления над данными из стакана и при 100 % нагрузки на процесор, вычисления занимают 3 сек, в этом случае Квик действительно повесится, если будете обрабатывать данные в OnQuote, необходимо отправлять вычисления в отдельный поток, через таблицу, как в коде, и тут в любом случае Вам придется создавать эти "лишние переменные" class и sec чтобы потом разобраться к какой бумаге относятся данные. Не задумывайтесь об этом, создать переменную и внеси в нее значение, это единицы микросекунд.
          Но отдельный поток не решает проблему отставания данных, он лишь только разгружает Квик, что бы он не вешался, но данные из стакана все равно будут с задержкой обработаны.
          Пример такой:
          На обработку данных из стакана нужно 3 секунды. Стакан за 1 секунду изменился 7 раз, это значит что данные нужно обработать 7 раз, по 3 сек, итого нужно затратить 21 сек и это время ни как не уменьшить. Если будете обрабатывать в функции OnQuote, повестите Квик на 21 секунду, если в отдельном потоке, тогда повестите поток на 21 сек. Конечно реализация в OnQuote "запрещена", Квик не отвиснет никогда, потому что пока он будет обрабатывать данные, за эти 21 сек придет еще 49 изменений стакана и он зависнет еще на 3*49=147 сек ну и так далее... В отдельном же потоке данные тоже будут появляться уже с опозданием, то есть алгоритм будет вычислять данные из стакана, который был 147 сек назад, а не реальный стакан. Тогда уже надо на несколько потоков разбивать вычисления, но не думаю что оно Вам надо ....
          Вообще выполняйте вычисления прям в OnQuote, как и советует kalikazandr, думаю хватит ресурсов процессора с головой.

          1. Не совсем так. Если функция OnQuote занята расчетом, то новый снепшот будет удален, события изменения стакана будут потеряны.
            И вообще расчеты стакана в квик это абсурдная затея. Это не ордербука фастовая, где видно - кто где насра...-выставил свою заявку на всем торговом диапазоне от pricemin до pricemax.
            Это снепшоты, которые не показывают всех изменений априори.
            По этому - пришел сигнал, допустим, с индикатора- взял стакан посмотрел - нормально! - заявка ушла или не ушла.

            1. На счет пропуска снимков, не подумал, действительно никаких снимков не будет, пока функция занята, но с другой стороны в другом потоке эти снимки копиться будут, если проц не будет успевать их обрабатывать...
              Ну да, в Квике стакан обрабатывать так себе затея...
              А вот OrderBook уже другое дело, но как то думаю дороговато для новичков.
              kalikazandr : " У меня бот работает на с++ " - Если не секрет то откуда OrderBook данные получает? И во сколько обходится? Давненько этой темой интересовался, но потом оставил, вроде как около 200 000 руб в год стоило данные получить если ничего не путаю.

              1. Нигде ничего не копится, даже OnOrder не придет, если в нем большой расчет - просто будет изменение внесено в таблицу заявок.
                Да, дороговато для не больших счетов, ко-локация + фикс_логин ~16 тыс. на так себе железе, и коннектор использую FIX Antenna C++, лицуха 5$тыс/год.
                И никто не берется сделать аналог, как ни плачевно.
                А вот данные получить как раз-таки бесплатно, если есть свой коннектор. Я за 5 лет не нашел умельца, кто-бы сделал.

                1. - "Нигде ничего не копится ..."
                  Я имел ввиду копятся в отдельном потоке. То есть в функции OnOrder снимки отправляем в таблицу, а эту таблицу обрабатываем в main, то есть в другом потоке.
                  В таком случае получается что если процессор будет успевать обрабатывать 10 снимков за 1 сек, а поступать будет 15 снимков в сек, в итоге таблица будет расти, снимки накапливаться. Или я чего то не понимаю...

          2. В том то и дело, что пока не реализую свой алгоритм полностью в коде, не смогу оценить скорости обработки всех событий. Еще хочу реализовать блок самооптимизации одного-двух параметров. А уже дальше увижу и оценю, стоит ли делать из робота "типа HFT" и апгрейд оборудования и прочей инфраструктуры, либо просто не зависящего от скоростей внутри дневного "торговца". ))

            1. Оно того стоит, если алгоритм генерирует прибыль, достаточную, на отбой затрат на инфраструктуру + чу-чуть себе).
              У меня бот работает на с++ под линукс, пока первый в рунете по скорости и стабилен, в отличии от любых lua решений под квик или аля-квик.
              Хотя в квике этот алгоритм тоже зарабатывает уже 7 лет, но не везде успевает.

  5. В моем случае скрипт линкует фейковый стакан по несуществующему инструменту RIM88.
    Проверка ==nil ничего не дает, параметры tb.bid_count, tb.offer_count имеют значение 0.

    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
    
    local class_code="SPBFUT"
    local sec_code="RIM88"
     
     
    function main()
     
    PrintDbgStr("\n\n-----\nMain\n-----")
     
    	Stakan(class_code,sec_code)
    end
     
     
    function Stakan(class_code, sec_code)
     
    PrintDbgStr(string.format("Stakan %s %s",class_code,sec_code))
     
    	tb=getQuoteLevel2(class_code, sec_code)
    	if tb==nil then
    PrintDbgStr("Ошибка инициализации стакана")
    	end
    PrintDbgStr(tostring(tb))
     
    PrintDbgStr(string.format("%s   ",os.date("%H:%M:%S")))
    PrintDbgStr(string.format("Bid: %d   Offer: %d\n\n",tb.bid_count,tb.offer_count))
     
    end

    Вот результат работы:

    [8224] -----
    [8224] Main
    [8224] -----
    [8224] Stakan SPBFUT RIM88
    [8224] table: 38EE3DC8
    [8224] 22:18:42
    [8224] Bid: 0 Offer: 0

    Хотел сделать проверку на открытие всех необходимых таблиц до запуска основной логики, но вот так не выходит.
    Что посоветуете?

    1. Из файла справки qlua.chm:
      TABLE getQuoteLevel2 (STRING class_code, STRING sec_code)

      Функция возвращает таблицу Lua с параметрами:

      При отсутствии и спроса и предложения функция возвращает таблицу без параметров bid и offer.

      1. Да getQuoteLevel2 в принципе не должна возвращать никакой таблице в этом случае, т. к. параметром передан несуществующий фьючерс RIM88, это в идеале.
        Если позволите поясню задачу. Есть список опционов - несколько десятков, а может и больше, который ежемесячно составляется вручную. При такой правке очень вероятны ошибки в названиях опционов - человеческий фактор. И я хотел сделать логическую проверку этих имен опционов и открытие (или хотя бы проверку открытия) соответствующих стаканов. Чем больше проверок до начала торговли, тем меньше сюрпризов во время торговли.

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

        1. Думаю, как раз, проверить на ошибку можно проверкой наличия полей bid и offer возвращаемой таблицы, а проверка наличия открытых стаканов, как и их программное открытие, невозможны стандартными средствами QLua, если только WinAPI использовать.

        1. Отнюдь. Если бы церковники не жгли книги, мы бы не пошли по технократическому пути.
          А неведение завело нас в тупик. Придумали бога и от его имени поубивали миллионы... Ушатали планету и т.п.
          Вот последствие "придумок"

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

            1. Интересное заключение.
              Т.е. ты утверждаешь,
              что в школе учителя должны дать ученикам Букварь и сказать - вот малыши - буквы, учите буквы, слова и т.д., или идите работать ковырялами.
              Ну и для пущей убедительности, объяснить - кто такие ковырялы (собственно примерно так наше образование и построено).
              Типа "государству" выгодно растить ковырял - нахера умные? - умные думают и не хотят работать на реально - чертей,
              которых расплодил наш бессрочный президент. Все счастливы, по телику, только в миру - нет.
              Вякать против него никто не сможет - дурни в кучу не могут собраться, разве что Халява какая позовет.
              Ну есть конечно вероятность, что кто-то из малышей станет умным при такой системе образования, только шанс 1:150 000 000 примерно. Зато у власть имущих все в ажуре - ебл-лицо все шире.
              Не прав ты Дмитрий.

              1. Ну вот и до политики добрались 🙂 Политика и религия запрещенные темы в общественных местах, так что не буду об этом. А вот в чем я не прав я так и не понял, надеюсь, что ты согласен с тем, что человека нужно учить думать, если хотим чтобы он стал умным и самостоятельным. Что тогда по твоему мнению нужно делать, чтобы человека заставить думать ? В моем понимании, человека нужно ставить перед проблемой, готового решения которой у него нет, но решить которую ему необходимо, только так у него появится необходимость думать, иначе он все время только и будет искать готовые решения, станет иждивенцем и столкнувшись с какой-то нестандартной задачей не сможет ее решить без посторонней помощи, т.к. не научился решать задачи самостоятельно. В чем я здесь не прав?

                1. Интересно девки пляшут,
                  возьми пахаря 40-ка летнего и скажи ему - вот ядерная электростанция - в ней происходит термоядерная реакция, но она достаточно не стабильна и может рвануть и накроется твое поле радиоактивными осадками и все погибнет.
                  Т.к. ты пахарь, лицо заинтересованное, то давай - придумай, как сделать термоядерный процесс контролируемым на 100%.
                  Что из этого получится? Правильно - ничего. Пахарь этот помрет и его правнуки тоже и так и не придумают - как термоядерный процесс сделать контролируемым.
                  Я считаю, если человек спрашивает, значит ему нужно.
                  И нужно ценить время этого человека. Конечно это прикольно, когда вбил в проблему пару месяцев и решил - герой,
                  только нафига изобретать велосипед?
                  А насчет школы - и обучения в целом частично - да. Но 1 башка хорошо, а 2 лучше и т.д. Тут один в поле не воин и от одного человека ничего не зависит вообще. Циалковский придумал ракету, однако он на ней никуда не полетел. На счет Джастас Уолкер - ну ему то делать совсем нечего он сидит себе дома, не обремененный заработком и сидит воспитывает детей - да он черт. 99% Россиян впахивают с утра до ночи на 3-х работах, что бы оплатить образование - что бы дети чувствовали себя лучше, а образования дети не получают, т.к. нет педагогов, как и врачей - одни фармацевты да барыги.

                  1. У Джастаса, кстати, ферма на 100 гектар семейная в Алтайском крае, он точно не бездельник. Знаешь как появлялся данный сайт и данная статья, в частности ? 🙂 Нужно мне как-то в начале моего изучения QLua получить данные из стакана, копался, ковырялся, получилось, чтобы снова потом не копаться, сохранил этот код с пояснениями в виде простенькой статьи, если у меня появляется необходимость перебрать стакан когда-то, я знаю, что у меня есть этот пример, в котором хорошо видна структура стакана, я его смотрю, если нужно, вспоминаю и использую частично в своем коде. Так и все остальные статьи появились и используются мной на данном сайте, как кусочки пазла, из которых можно собрать то, что нужно. Это не готовая программа ВУЗа, это просто мой блокнот, в котором я сохранил все необходимое, но потом, почему-то, его стали просматривать и другие, говорить что им с этим блокнотом тоже жить стало легче, я рад этому, по этому не понимаю что ты пытаешься мне сказать, или в чем упрекнуть 🙂

                    1. Пусть каждый останется при своем мнении на счет того как должно происходить обучение и почему в России оно за деньги.
                      А Джастас Уолкер вообще с какого перепуга заимел 100 га? По логике вещей, он лимита, не имеет никаких прав в нашей стране и должен был работать на стройке, рядом с таджиками и узбеками или янки чем то лучше?
                      Ах да - баксы же у янок. Трудился он там - звукозапись, пилорама. Родители миссионеры - свои нахер посылали - к нам приехали отбирать у народа гроши последние.
                      Что то узбеки к нам приезжали и никто не смог занялся фермерством или собственным производством, их вообще за людей то не считали, а гноили на стройках да помойках.
                      Так что не все так хорошо с этим Джастас, как выяснилось.
                      А насчет трейдинга и то что он не делает мир лучше, не правда твоя.
                      Мне в радость честно отобрать ворованные деньги и поделиться с государством, игрушек в садик купить и т.д и т.п.

                  2. Я согласен, что у нас в России многое не так, но я считаю самым эффективным способом что-то менять, это только на своем примере показывать как нужно жить, по этому стараюсь никого не винить, а сам становиться лучше и правильнее, трейдинг и программирование роботов для меня, кстати, давно ушли на десятый план, т.к. это не делает мир вокруг лучше, ничего в него не привносит, по этому, вообще этим не занимаюсь уже давно, иногда только индивидуально учу людей как скрипт запрограммить какой-нить и все.

              2. По поводу букваря, считаю, что в школе должны не информацию стандартную в голову запихивать детям, а прививать тягу к познанию, желание и умение думать, самостоятельно решать задачи. Об этом хорошо рассказывает один, на мой взгляд, интересный ютуб блогер Джастас Уолкер, у него целый плейлист есть про семейное образование, там, на мой взгляд, все разумно объясняется, так что не буду его пересказывать, если интересна эта проблема, рекомендую посмотреть и взять на вооружение.

  6. Здравствуйте, у меня вопрос-должен ли стакан быть открыт чтобы луа мог прочитать стакан?
    У меня почемуто при закрытом стакане не работает.

          1. Еще раз спасибо! Думаю организовать маленький проверочный цикл-мол если стакан он прочитать не может то заказываем и снова по кругу пока не прочитает.

  7. Привет. Сделал проверку на nil таким образом, результат - все равно вырубается робот

    1
    2
    3
    4
    5
    
          C_Glass = getQuoteLevel2("SPBOPT", Call) -- Стакан
          if C_Glass ~= nil then -- 2я цена в стакане
             if C_Glass.bid[tonumber(C_Glass.bid_count)-1].price ~= nil then C_Bid_2 = tonumber(C_Glass.bid[tonumber(C_Glass.bid_count)-1].price) else C_Bid_2 = 0 end
          else C_Bid_2 = 0
          end
      1. да, в 3 стр. пишет ошибка, это возникает после клиринга вечернего - точно - дневной не помню и сегодня с утра терминал запустил как обычно (робот был включен), после обновления данных - отключился

          1. а понял, значит надо все эти строки на нил проверять? я то думал

            1
            2
            
                  C_Glass = getQuoteLevel2("SPBOPT", Call) -- Стакан
                  if C_Glass ~= nil then -- 2я цена в стакане

            этого достаточно

                  1. Чё то делал со стаканом год назад, а что делал уже не помню и точно помню его обозвал Glass - поискал не нашел - удалил вероятно - обычно то все сохраняю в папке Примеры - что бы потом с примера уже быстренько вспоминать что к чему и делать - щас вот задача есть стакан смотреть весь - а что к чему все забыл - вот к тебе зашел - и свой же пример увидел - еще раз говорю - ты молодец очень большой - твой ресурс КЛАСС - меня щас люди спрашивают чё да как бы самому научится ботов писать - всех сюда отправляю

  8. Добрый день!
    Не получается считать данные из стакана, код следующий:

    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
    28
    29
    30
    31
    32
    33
    34
    35
    
     IsRun = true
    time = os.clock()
    class ="SPBFUT"
    sec = "SiH7"
    --[[ function OnParam(class, sec)
       if (class =="SPBFUT" and  sec == "SRH7") then
          tbid = tonumber(getParamEx(class, sec, "bid").param_value)
          tbidvol = tonumber(getParamEx(class, sec, "BIDDEPTH").param_value)
          if tbidvol ~= 0 then
             message(string.format('Цена покупки в таблице %d , объем %0.0f ,Время %0.3f сек',tbid,tbidvol, os.clock()-time), 1)
          else message(string.format("Нет покупки в таблице, время %0.3f сек",os.clock()-time),1)
          end
       end
     end   ]]
     function OnQuote(class, sec )
       if (class =="SPBFUT" and  sec == "SiH7") then
          ql2 = getQuoteLevel2(class, sec)
          if ql2 ~= nil then
             gbid = tonumber(ql2.bid[ql2.bid_count].price)
             gbidvol = tonumber(ql2.bid[ql2.bid_count].quantity)
             message(string.format('Цена покупки в стакане %d , объем %0.0f, Время %0.3f сек',gbid,gbidvol, os.clock()-time), 1)
          else message(string.format("Нет покупки в стакане, время %0.3f сек",os.clock()-time),1)
          end
       end
     end
     
    function main()
       while IsRun do
          sleep(10) -- Пауза 10 мс, чтобы не грузить сильно процессор
       end
    end
     
    function OnStop()
       IsRun = false
    end

    Все время идет по варианту ql2 == nil, т.е. выдает сообщение :"Нет покупки в стакане...", стакан открыт и данные в нем меняются. Если убрать сравнение с nil - выскакивает ошибка.
    При этом из "Текущей таблицы параметров" все считывается прекрасно - закомментированная часть кода.

    1. Вот так попробуйте:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      
       function OnQuote(_class, _sec )
         if (_class =="SPBFUT" and  _sec == "SiH7") then
            ql2 = getQuoteLevel2(_class, _sec)
            if ql2 ~= nil then
               gbid = tonumber(ql2.bid[ql2.bid_count].price)
               gbidvol = tonumber(ql2.bid[ql2.bid_count].quantity)
               message(string.format('Цена покупки в стакане %d , объем %0.0f, Время %0.3f сек',gbid,gbidvol, os.clock()-time), 1)
            else message(string.format("Нет покупки в стакане, время %0.3f сек",os.clock()-time),1)
            end
         end
       end

      У Вас здесь, скорее всего, конфликт в названии переменных. В OnParam Вы, кстати, другой инструмент смотрите.

      1. Поменял, теперь выскакивает ошибка: Test3.lua:19: attempt to index field '?' (a nil value).
        Этого я совсем не понимаю:(, ведь я ее с nil и сравниваю.
        Подскажите, что принципиально меняет символ подчеркивания: "_class" вместо "class"? Я думал, это просто разные названия переменных, но по-видимому я ошибаюсь?
        Какой конфликт в названии переменных Вы имеете ввиду? Если: class ="SPBFUT"
        sec = "SiH7", то это ничего не меняет, я могу их просто закомментировать.
        В OnParam (да и в OnQuote) я смотрел разные инструменты, в том числе и этот, но в 1-м случае читаются все переменные, во вторм ни один:(.

        1. Подчеркивание просто другое имя дает, т.к. у Вас в начале скрипта объявлены переменные с такими же именами, что Вы в функции OnQuote использовали:
          строки 3,4
          class ="SPBFUT"
          sec = "SiH7"

          можете вместо символа подчеркивания любую букву использовать, или удалить строки 3,4 просто

          По поводу ошибки, то Вы проверяете только ql2 на nil, а он не nil, но, возможно, nil'ом является bid, или bid_count, или price.
          Ведь в этой статье есть пример, воспользуйтесь им и подкорректируйте под себя.

        2. Теперь вообще началась мистика!
          Вернул все к первозданному виду, но вместо сообщения :"Нет покупки в стакане...", идет ошибка: "Test3.lua:19: attempt to index field '?' (a nil value)."
          Как такое возможно??? Квик перегружал - не помогает.
          Решил, что где-то ошибся при восстановлении текста - просто скопировал свое первое сообщение отсюда и запустил - снова та же ошибка???

          1. строки 3,4
            class ="SPBFUT"
            sec = "SiH7"
            я просто закомментировал - результат тот-же.
            Естественно ql2 не nil, так как стакан полон, и так же естественно, что bid, или bid_count, или price тоже не nil, по той же самой причине. Но почему тогда ошибка: Test3.lua:19: attempt to index field '?' (a nil value).

            1. Если бы они были все не nil, то не было бы ошибки, Вы не гадайте, а выведите каждый из этих параметров в message, только оберните в tostring, потому что иначе nil не выведется. А конфликта переменных значит не было.

              1. разобрался, строка должна выглядеть так:
                "gbid = tonumber(ql2.bid[tonumber(ql2.bid_count)].price)"
                а то я уже решил, что у меня глюки начались:).
                Правда остается непонятным, почему первоначальный код работал по варианту ql2 == nil, ведь стаканы не были пустыми?
                И, кстати, вопрос: если первоначально определять переменные class и sec, то function OnQuote(class, sec ) будет срабатывать только в случае указанного стакана или при изменении любого стакана открытого в Квике?

        1. по идее, это значение в текущей таблице параметров должно отображаться, но у меня, почему-то, не отображается, значит только в цикле перебирать стакан и считать сумму лотов во всех заявках на покупку, или на продажу

          1. А из стакана почему напрямую нельзя?Нужен объем по стакану(индикатор нарастающего объема).ТТП тут не поможет,она ведь весь стакан не отображает.Получается из стакана только два параметра можно вызвать Цена и Количество?

            1. Уточню,может я плохо сформулировала вопрос,есть два параметра в стакане
              price -- Цена покупки / продажи (STRING)
              quantity -- Количество в лотах (STRING)

              Как называется колонка Сумма лучших(STRING)?Этот параметр отображается в стакане при добавлении Параметра вручную.

            2. цена и количество, но их можно взять по каждой цене, которая есть в стакане, если посчитать сумму всех количеств в заявках на покупку, находящихся в данный момент в стакане, то получится то, что Вам нужно.
              bid и offer это массивы, каждый элемент которых имеет поля price и quantity, вверху же есть пример