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

Автор записи: Дмитрий (Admin)
1 звезда2 звезды3 звезды4 звезды5 звезд (Голосов 3, среднее: 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): 109 комментариев

  1. Как-то не заметил, вот такой пример, Дмитрий - будет создавать задержки получения данных терминалом.
    При повышенной волатильности, это может быть заметно визуально.
    Особенно не стоит делать какие-то серьезные вычисления в функциях обратного вызова: 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 &gt; 0 do -- обрабатываем все накопленные изменения стакана
    	OnQuoteDo(table.sremove(QuoteThread, 1))
            -- вот тут никаких sleep - ов не нужно, обрабатываем, максимально используя проц.
        end
        sleep(1)
      end
    end
    function OnStop()
      is_run = false
      return 1000
    end

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

  2. В моем случае скрипт линкует фейковый стакан по несуществующему инструменту 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. По поводу букваря, считаю, что в школе должны не информацию стандартную в голову запихивать детям, а прививать тягу к познанию, желание и умение думать, самостоятельно решать задачи. Об этом хорошо рассказывает один, на мой взгляд, интересный ютуб блогер Джастас Уолкер, у него целый плейлист есть про семейное образование, там, на мой взгляд, все разумно объясняется, так что не буду его пересказывать, если интересна эта проблема, рекомендую посмотреть и взять на вооружение.

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

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

  4. Привет. Сделал проверку на 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 - поискал не нашел - удалил вероятно - обычно то все сохраняю в папке Примеры - что бы потом с примера уже быстренько вспоминать что к чему и делать - щас вот задача есть стакан смотреть весь - а что к чему все забыл - вот к тебе зашел - и свой же пример увидел - еще раз говорю - ты молодец очень большой - твой ресурс КЛАСС - меня щас люди спрашивают чё да как бы самому научится ботов писать - всех сюда отправляю

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

    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, вверху же есть пример