Получение данных из таблиц QUIK в QLua(Lua)

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

QUIK-Qlua-poluchenie-dannyh Для получения данных из таблиц терминала QUIK удобно пользоваться 3-мя функциями: getItem(), getNumberOf() и getParamEx().

Пример:

-- Перебирает строки таблицы "Позиции по клиентским счетам (фьючерсы)", ищет Текущие чистые позиции по инструменту "RIH5"
for i = 0,getNumberOf("FUTURES_CLIENT_HOLDING") - 1 do
   -- ЕСЛИ строка по нужному инструменту И чистая позиция не равна нулю ТО
   if getItem("FUTURES_CLIENT_HOLDING",i).sec_code == "RIH5" and getItem("FUTURES_CLIENT_HOLDING",i).totalnet ~= 0 then
      -- ЕСЛИ текущая чистая позиция > 0, ТО открыта длинная позиция (BUY)
      if getItem("FUTURES_CLIENT_HOLDING",i).totalnet > 0 then 
         IsBuy = true;
         BuyVol = getItem("FUTURES_CLIENT_HOLDING",i).totalnet;	-- Количество лотов в позиции BUY				
      else   -- ИНАЧЕ открыта короткая позиция (SELL)
         IsSell = true;
         SellVol = math.abs(getItem("FUTURES_CLIENT_HOLDING",i).totalnet); -- Количество лотов в позиции SELL
      end;
   end;
end;

Далее перечислены таблицы, их идентификаторы и поля, к которым можно обращаться:

Фирмы:
Классы:
Инструменты:
Торговые счета:
Коды клиентов:
Обезличенные сделки (Таблица всех сделок):
Денежные позиции:
Заявки:
Позиции по клиентским счетам (фьючерсы):
Лимиты по фьючерсам:
Лимиты по денежным средствам:
Лимиты по бумагам:
Сделки:
Стоп-заявки:
Заявки на внебиржевые сделки:
Сделки для исполнения:
Отчеты по сделкам для исполнения:
Текущие позиции по бумагам:
Текущие позиции по клиентским счетам:
Обязательства и требования по деньгам:
Обязательства и требования по активам:
Для получения значений всех параметров биржевой информации из таблицы "Текущие торги" существует специальная функция
getParamEx (STRING class_code, STRING sec_code, STRING param_name),
которая принимает 3 параметра:
   - Код класса
   - Код инструмента
   - Имя параметра из таблицы "Текущие торги".

Функция возвращает таблицу Lua со следующими полями:
   param_type    STRING Тип данных параметра, используемый в таблице "Текущие торги". Возможные значения: 
      «1» - DOUBLE ,
      «2» - LONG, 
      «3» - CHAR, 
      «4» - перечислимый тип, 
      «5» - время, 
      «6» - дата 
 
   param_value   STRING Значение параметра. Для param_type = 3 значение параметра равно «0», в остальных случаях – числовое представление. Для перечислимых типов значение равно порядковому значению перечисления  

   param_image   STRING Строковое значение параметра, аналогичное его представлению в таблице. В строковом представлении учитываются разделители разрядов, разделители целой и дробной части. Для перечислимых типов выводятся соответствующие им строковые значения

Пример использования:

Status =  tonumber(getParamEx("SPBFUT",  "RIM5", "STATUS").param_value);
-- Выводит сообщение о текущем состоянии
if Status == 1 then message("RIM5 торгуется"); else message("RIM5 не торгуется"); end;
Список возможных идентификаторов параметров, передаваемых в функцию getParamEx()

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

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

Получение данных из таблиц QUIK в QLua(Lua): 500 комментариев

  1. День добрый!
    Просьба к знающим посмотреть.
    local tblAsk = getParamEx(CLASS_CODE, SEC_CODE, "OFFER")
    local tblBid = getParamEx(CLASS_CODE, SEC_CODE, "BID")
    local tblStatus = getParamEx(CLASS_CODE, SEC_CODE, "STATUS")
    local tblMinLot = getParamEx(CLASS_CODE, SEC_CODE, "LOTSIZE")
    local tblBalance = getMoneyEx(FIRM_ID, CLIENT_CODE, TAG, CURR_CODE,LIMIT_KIND)
    local tblTotalLot = getDepoEx(FIRM_ID, CLIENT_CODE, SEC_CODE, ACCOUNT ,LIMIT_KIND)
    local tblMinStepPrice = getParamEx(CLASS_CODE, SEC_CODE, "SEC_PRICE_STEP")

    if tblAsk==nil then Ask=0 else Ask=tonumber(tblAsk.param_value) end
    if tblBid==nil then Bid=0 else Bid=tonumber(tblBid.param_value) end
    if tblStatus==nil then Status=0 else Status=tonumber(tblStatus.param_value) end
    if tblMinLot==nil then MinLot=0 else MinLot=tonumber(tblMinLot.param_value) end
    if tblBalance==nil then Balance=0 else Balance=tonumber(tblBalance.currentbal) end
    if tblTotalLot==nil then TotalLots=0 else TotalLots=tonumber(tblTotalLot.currentbal) end
    if tblMinStepPrice==nil then MinStepPrice=0 else MinStepPrice=tonumber(tblMinStepPrice.param_value) end

    Правильно ли я пытаюсь получить данные из таблиц в переменные и в особенности TotalLots. Заранее спасибо)

  2. Добрый вечер! Помогите решить вопрос: в итоге кода в переменной тикера сообщение всегда выдаёт nil!?

    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
    
    -- поиск ближайших фьючерсов
    	for i = 0,getNumberOf("securities") - 1 do -- перебираем инструменты
    		if getItem("securities",i).class_code == "SPBFUT" -- если это фьючерс
    			and (string.sub(getItem("securities",i).code, 1,2)) == "Si" then -- содержит Si
    			local sec_code = getItem("securities",i).code -- получаем тикер
    			local mat_days = getParamEx("SPBFUT",sec_code,"DAYS_TO_MAT_DATE").param_value -- получаем дни до погашения
    			if tonumber(mat_days) > 2 then -- если дней до погашения больше двух
    				Si_code[#Si_code+1] =
    					{
    					Code = tostring(sec_code), --занесение в таблицу тикера
    					Days = tonumber(mat_days), -- занесение в таблицу дней до экпир-ции
    					}
    			end
    		end
    	end
    --нахождение в таблице тикеров ближайшего фьючерса
    	for i = 1, #Si_code do
    		min_FUT = Si_code[1].Days
    		if Si_code[i].Days < min_FUT then
    			min_FUT = Si_code[i].Days -- переменная минимального количества дней
    			min_code = Si_code[i].Code -- переменная с тикером min_FUT
    		end
    	end
    		message(tostring(min_code)) --ВОТ ТУТ ВСЕГДА nil  !!!!
    		message(tostring(min_FUT))
    1. Notepad ++ Да. В Меню синтаксис нужно включить Lua. В интернете по запросу поищите настройки Луа. Там библиотеку надо подключить в плагинах. Не помню точно, какие-то "птички" поставить. Но SciTE мне больше понравился.

  3. Всем доброй ночи! Помогите, пожалуйста, разобраться?!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    	for i = 0,getNumberOf("securities") - 1 do -- перебираем инструменты
    		if getItem("securities",i).class_code == "SPBFUT" -- если это фьючерс
    			and (string.sub(getItem("securities",i).code, 1,2)) == "Si" then -- содержит Si
    				sec_code = getItem("securities",i).code -- получаем тикер
    				mat_days = getParamEx("SPBFUT",sec_code,"DAYS_TO_MAT_DATE").param_value -- получаем дни до погашения
    				if mat_days > 2 then -- если дней до погашения больше двух
    				-- ТУТ ВЫХОДИТ ОШИБКА !!! "attempt to compare number with string" СРАВНЕНИЯ ЧИСЛА СО СТРОКОЙ
    					message(sec_code)
    					message(mat_days)
    				end
    		end
    	end
    1. я не спец в LUA, но у меня похожая часть функционирует со следующими буковками. Дальше сравнивается номер с номером.

      1
      
      tonumber(getParamEx(Class,Emit,"DAYS_TO_MAT_DATE").param_value)
  4. Здравствуйте всем.
    "Приспичило" 8-))) уже голова набекрень с комиссией биржи.
    Раньше была возможность получить комиссию ТС для робота через "exch_pay"
    например:

     getParamEx(class_code, sec_code, "exch_pay")
     
    2 года назад "лавочку прикрыли"
    Пробовал 
     "exchange_comission"
     , 
     "tech_center_comission"
     .  Тоже голяк.
    Можно ли как-нибудь до сделки увидеть сумму комиссии. Я на Кваке 7.29. На Восьмёрку пока не перешёл. Возможно здесь "собака зарыта". Подскажите пожалуйста.
      1. Здравствуйте, toxa.

        Пожалуйста, подскажите где можно формулу посмотреть или хотя бы ссылку, где можно прочитать, что используется при расчёте.

        1. ну вы даже не написали, чем торгуете. предположим, вы торгуете на фондовом рынке на московской бирже. идете на сайт биржи, открываете для нужного рынка раздел "тарифы": http://www.moex.com/s1197 далее при помощи коэффициентов из таблички получаете нужное значение. на самом деле, вам не обязательно точно вычислять, достаточно иметь оценку сверху: "комиссия будет не более чем". потом, когда придут сделки - скорректируете деньги.

        2. Разобрался.
          FutFee = Round ( Round ( FutPrice х Round ( W(f) / R(f) ;5 ) ;2 ) х BaseFutFee ;2)

          FutFee ≥ 0,01 руб

          где,
          FutFee - величина биржевой комиссии за заключение фьючерса (в рублях);
          FutPrice - значение цены фьючерса (в единицах измерения, в которых указывается цена фьючерса в заявке);
          W(f) - стоимость минимального шага цены фьючерса (в рублях);
          R(f) - минимальный шаг цены фьючерса;
          Round - функция математического округления с заданной точностью;
          BaseFutFee - значение базовой ставки тарифа за заключение фьючерса для Группы Срочных контрактов, к которой относится данный фьючерс (в базисных пунктах).

          Группы контрактов по типам базовых активов
          Фондовые контракты = 0,00660
          Товарные контракты = 0,00440

          Эта инфа с биржи. Может кому-то пригодиться

            1. Я в Атоне. Комиссия биржи это вся комиссия на фьючах. Сколько берёт брокер от этой суммы мне не важно. Есть еще ежемесячная сумма за обслуживание счета кажется 175 руб. и всё.
              По поводу группы контрактов по типам базовых активов - их 5 штук я привёл 2. Есть еще: Валютные контракты, Процентные контракты, Товарные контракты

              1. Извиняюсь ошибся дважды.
                1. Торговля фьючерсами.: https://www.moex.com/s93 . Отсюда я и взял формулу для расчёта (правда на 1 копейку не сходится с цифрой которая "приезжает" вместе со сделкой)
                2. С брокерской комиссией я ошибся. Брокер удваивает комиссию биржи. Т.е. 1-я доля биржевая комиссия, вторая (равна первой) комиссия брокера. Скальпинг естественно увеличивает комиссию на 0.5

                1. Ещё один вопрос. (Срочный рынок. Формулы с предыдущего поста.) Экспериментально определил, что комиссия высчитывается один раз в день по цене закрытия дневной сессии в 18:45 МСК и действует на вечернюю сессию и на следующий день. Сам вопрос. Как получить эту цену. Перепробовал методом "научного тыка" все виды комиссий используемых в getParamEx. Безрезультатно. Формула ниже не подходит (цифры другие! Как я понял тут цена окончания торгового дня, т.е. закрытие вечерней сессии).

                  1
                  
                   tonumber(getParamEx(Class,Emit,"PREVPRICE").param_value)

                  Может кто-нибудь подскажет направление поисков хотя бы.

                  1. предполагаю, что у вас не бьется комиссия по RI. скорее всего, проблема в том, что он в баксах, а "W(f) стоимость минимального шага цены фьючерса (в рублях)" - величина не постоянная и меняется, как минимум, в клиринг, промклиринг. при чем в клиринг берется значение на 16:30. опять же, не зная, чем вы пытаетесь торговать, понять, где вы ошибаетесь довольно тяжело.

                    1. Торгую фьючерсами. Все алгоритмы обкатываю на реале Сбербанка (SRH0). Долларовые активы - стараюсь пока не роботизировать по ряду причин. Не получается представить весь механизм робота. Пишу по возможности (времени) небольшими блоками. Читая вопросы по теме от других пользователей складывается впечатление, что все пишут примерно одно и тоже, но у каждого своя база знаний и понимания с какой стороны подойти к проблеме.

  5. Добрый вечер. Может кто подскажет каким идентификатором вытащить "листинг" облигации. В таблице "Текущие торги" он есть, а как вытащить не нашел нигде. Спасибо

    1. LISTLEVEL?

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      
      -- securities parameters list (QUIK 8.1):
      --
      -- LONGNAME, SHORTNAME, CODE, ISINCODE, REGNUMBER, CFI_CODE, CLASSNAME, CLASS_CODE, TRADE_DATE_CODE, SEC_FACE_VALUE, SEC_FACE_UNIT, SEC_SCALE, LCURRENTPRICE,
      -- SEC_PRICE_STEP, DISCOUNT1, DISCOUNT2, DISCOUNT3, SEC_COMMENT, LOTSIZE, SECTYPE, CURRENCYID, LISTLEVEL, PRIMARYDIST, QUALIFIED, ASSURED, ANONTRADE, STATUS, 
      -- BID, BIDDEPTH, BIDDEPTHT, NUMBIDS, OFFER, OFFERDEPTH, OFFERDEPTHT, NUMOFFERS, OPEN, HIGH, LOW, LAST, CHANGE, QTY, TIME, VOLTODAY, VALTODAY, TRADINGSTATUS, 
      -- VALUE, WAPRICE, HIGHBID, LOWOFFER, NUMTRADES, PREVPRICE, PREVWAPRICE, CLOSEPRICE, LASTCHANGE, MARKETPRICE, PRICEMAX, PRICEMIN, SELLDEPO, BUYDEPO, STEPPRICET, 
      -- STEPPRICE, CLPRICE, STARTTIME, ENDTIME, MAT_DATE, DAYS_TO_MAT_DATE, OPTIONBASECLASS, R_SETTLEPRICE, OPENPCTCHANGE, STEP_IN_CURRENCY, PRICEMINUSPREVWA,  
      -- SETTLEDATE, SETTLECODE, SETTLEDATE1, TRADINGPHASE, FIRST_CUR, SECOND_CUR, DPVALINDICATORBU, DPVALINDICATORSE, MARKETPRICETODAY, ISSUESIZE, PREVDATE, BASEPRICE, 
      -- LCLOSEPRICE, QUOTEBASIS, ADMITTEDQUOTE, PREVADMITTEDQUOT, LASTBID, LASTOFFER, MARKETPRICE2, PREVLEGALCLOSEPR, OPENPERIODPRICE, MIN_CURR_LAST, MIN_CURR_LAST_TI, 
      -- ISSUESIZEPLACED, COUNTERPRICE, PREVLOTSIZE, LOTSIZECHANGEDAT, PLANNEDTIME, AUCTPRICE, AUCTVALUE, AUCTVOLUME, AUCTNUMTRADES, IMBALANCE, MARKETVOLB, MARKETVOLS
  6. Добрый день. Какой функцией (как можно проще) узнать цену по акции и фьючерсу, если робот будет совершать сделки по рынку. А может есть функция чтобы узнать среднюю цену?

    1. BID,OFFER,WAPRICE. могут еще понадобиться нижний/верхний лимиты цен, так как на фортсе нет рыночных заявок и их нужно эмулировать лимитками: PRICEMAX и PRICEMIN

      1. Я не правильно вопрос задал я покупаю по рынку следующим скриптом, а в ответном сообщение цена = 0, т.к. по рынку куплена. Я думал может использовать какую то функцию, чтобы брать цену из таблицы сделок. Но честно говоря вообще запутался в этих функция, мне надо потом еще использовать полученную цену, если не сложно посоветуйте функцию или может пример использования. Может есть какая то функция показывающая цену как акции так и фьючерса?

        -- Код класса
        class_code = "TQBR" -- НАДО МЕНЯТЬ В ЗАВИСИМОСТИ ЧТО ХОЧЕШЬ КУПИТЬ ПРОДАТЬ
        -- Код бумаги
        sec_code = "HYDR" -- ВЫБИРАЕШЬ ИНСТРУМЕНТ
        -- Номер счета
        account = "L01+00000F00" -- БЕРЕТСЯ ИЗ ОКНА ВЫСТАВЛЕНИЯ ЗАЯВКИ

        -- Классы фьючерсов и опционов
        FUT_OPT_CLASSES = "FUTUX, OPTUX, SPBOPT, SPBFUT"

        -- Уникальное число берется из текущей полной даты и времени.
        -- Используется для получения случайного числа
        RANDOM_SEED = tonumber(os.date("%Y%m%d%H%M%S"))

        function main()
        -- Вызываем функцию отправки рыночной заявки на покупку. 1 контракт
        local trans_id, trans_msg = send_market("B", 1) -- ВЫБИРАЕМ SELL ИЛИ BUY
        -- Выводим результат в виде сообщения
        message("trans_id: " .. tostring(trans_id) .. "; trans_msg: " .. tostring(trans_msg))
        end

        -- Функция для отравки рыночной заявки
        function send_market(direction, volume, comment)
        -- отправка рыночной заявки
        -- все параметры кроме кода клиента и коментария должны быть не nil
        -- если код клиента nil - подлставляем счет
        -- Данная функция возвращает 2 параметра
        -- 1. ID присвоенный транзакции либо nil если транзакция отвергнута на уровне сервера Квик
        -- 2. Ответное сообщение сервера Квик либо строку с параметрами транзакции
        if (class_code == nil or sec_code == nil or direction == nil or volume == nil or account == nil) then
        return nil, "Can`t send order. Nil parameters."
        end

        -- Получаем случайное уникальное число для id
        local trans_id = random_max()
        -- Таблица параметров транзацкии
        local transaction={
        ["TRANS_ID"] = tostring(trans_id),
        ["ACTION"] = "NEW_ORDER",
        ["CLASSCODE"] = class_code,
        ["SECCODE"] = sec_code,
        ["OPERATION"] = direction,
        ["TYPE"] = "M",
        ["QUANTITY"] = string.format("%d", tostring(volume)),
        ["ACCOUNT"] = account
        }
        -- Если нет кода клиента, то вместо него подставляем номер счета
        if client_code == nil then
        transaction.client_code = account
        else
        transaction.client_code = client_code
        end

        -- Если это заявка для Фьючерсов
        if string.find(FUT_OPT_CLASSES, class_code) ~= nil then
        local sign = 0
        -- Для покупки
        if direction == "B" then
        sign = 1
        -- Получаем максимальную цену
        transaction.price = getParamEx(class_code, sec_code, "pricemax").param_value
        -- Если текущая цена 0
        if transaction.price == 0 then
        -- Берем цену предложения и добавляем 10 шагов
        transaction.price = getParamEx(class_code, sec_code, "offer").param_value + 10 * getParamEx(class_code, sec_code, "SEC_PRICE_STEP").param_value
        end
        else
        sign = -1
        -- Для продажи берем минимальную цену
        transaction.price = getParamEx(class_code, sec_code, "pricemin").param_value
        -- Если текущая цена 0
        if transaction.price == 0 then
        -- Берем цену Bid и отнимаем 10 шагов
        transaction.price = getParamEx(class_code, sec_code, "bid").param_value - 10 * getParamEx(class_code, sec_code, "SEC_PRICE_STEP").param_value
        end
        end
        -- Если цена так и не установилась
        if transaction.price == 0 then
        -- То пытаемся отнять/прибавть 10 шагов к последней известной цене
        transaction.price = getParamEx(class_code, sec_code, "last").param_value + sign * 10 * getParamEx(class_code, sec_code, "SEC_PRICE_STEP").param_value
        end
        -- Форматируем цену
        transaction.price = to_price(sec_code, transaction.price, class_code)
        else
        transaction.price = "0"
        end

        -- Если комментарий не указан
        if comment ~= nil then
        transaction.client_code = string.sub(transaction.client_code .. '/' .. tostring(comment), 0, 20)
        else
        transaction.client_code = string.sub(transaction.client_code, 0, 20)
        end

        -- Отправляем транзацкию
        local res = sendTransaction(transaction)
        -- Если результат не пустой
        if res ~= "" then
        return nil, res
        else
        local msg =
        "Market order sended sucesfully. Class = " .. class_code ..
        " Sec = " .. sec_code ..
        " Dir = " .. direction ..
        " Vol = " .. volume ..
        " Acc = " .. account ..
        " Trans_id = " .. trans_id ..
        " Price = " .. transaction.price

        return trans_id, msg
        end
        end

        function random_max()
        -- не принимает параметры и возвращает от 0 до 2147483647 (макс. полож. 32 битное число) подходит нам для транзакций
        local res = (16807*(RANDOM_SEED or 137137))%2147483647
        RANDOM_SEED = res
        return res
        end

        function to_price(security, value, class)
        -- преобразования значения value к цене инструмента правильного ФОРМАТА (обрезаем лишнии знаки после разделителя)
        -- Возвращает строку
        if (security == nil or value == nil) then return nil end
        local scale = getSecurityInfo(class or getSecurityInfo("", security).class_code, security).scale
        return string.format("%."..string.format("%d", scale).."f", tonumber(value))
        end

        1. к сожалению, конкретная цена исполнения не может быть известна заранее, это свойство рыночной заявки "исполнять по любым ценам". вы можете посылать лимитную заявку с признаком "снять остаток", и тогда цена, которую вы указываете, будет "оценкой снизу": сделки будут по этой указанной цене или лучше. либо вы можете обрабатывать колбэк OnTrade и накапливать сделки по заявке, после чего по этим сделкам можно легко вычислить цену, которая получилась. другого способа нет.

          1. Мне не надо заранее цену знать, я потом не могу узнать и понять какая функция самая простая OnTrade (в ней столько параметров и еще какие битовые флаги!!!, она показывает и фьючи и акции?) или может для акций предпочтительней выбрать OnDepoLimit (нет примера толкового) а для фьючерсов OnFuturesClientHolding (примеров тоже нет). Хочется сделать больше универсальности и как можно проще без изысков лишних.

            1. Я не очень понимаю, что вы подразумеваете под " узнать цену по акции и фьючерсу". Вы написали три камента, но яснее не стало. 🙂 Вы хотите получить среднюю цену исполнения каждой заявки? Или среднюю цену позиции по инструменту? Если ваш робот ставит заявку "по рынку" (или лимитку. или не робот, а вы ставите руками, или если кто-то, у кого есть доступ к вашему счету через другой квик, и по этой заявке возникает сделка), то сделки попадут в колбэк OnTrade (и по акциям, и по фьючам и по любым доступным инструментам). Далее, агрегируя приходящие сделки по номеру заявки, вы можете посчитать среднюю цену исполнения по любой новой заявке, а так же среднюю цену позиции, если агрегировать по инструменту. В OnTrade попадают только новые сделки, которые совершены, когда скрипт уже запущен. Чтобы посчитать что-то по прошлым данным, то вам нужно пролистать таблицу Trade. Формат строк в таблице Trade и в колбэке OnTrade - одинаковый, что упрощает задачу.

              1. Подскажите какие параметры нужно вписать в этот OnTrade? чтобы он выдал цену сделки к примеру по сберу. class_code, sec_code, trans_id? Никак не могу добиться цены((( Ни одной толковой книжки нет, купил курсы какие то тупые с арифметикой.

                1. не вы эту функцию вызываете. вы ее в коде объявляете, и дальше квик эту функцию вызывает. отвечая на ваш вопрос:

                  1
                  2
                  3
                  4
                  5
                  
                  function OnTrade(trade)
                    if trade.sec_code == "SBER" then 
                      message(tostring(trade.price), 0)
                    end
                  end

                  вы объявляете функцию в скрипте, и ждете, пока квик ее вызовет.

                    1. Добрый день. Скажите какой функцией, таблицей или еще чем добиться, что это г.... ontrade ОДНУ среднюю цену

                      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
                      36
                      37
                      38
                      39
                      
                      is_run = true				
                      Sec_code = "SBER"			
                      Table1 = {}
                      Table2 = {}
                       
                       
                      function main()				
                          while is_run do
                              sleep(50)
                          end;
                      end
                       
                      function OnStop()			
                          is_run = false
                      end
                       
                      function OnTrade(trade)
                      local itog = 0;
                      local posl = 0;
                      local sredcen = 0;
                      local i = 0
                       
                        if trade.sec_code == Sec_code then 
                          	i = i + 1 
                      		Table1[i] = tostring(trade.price)
                      		Table2[i] = tostring(trade.qty)
                       
                      sleep(10)
                       
                      	for n = 1, #Table1, 1 do
                      	itog = Table1[n]*Table2[n] + itog
                      	posl = posl + Table2[n]
                      	end
                       
                      sredcen = itog/posl
                      message((sredcen), 1)	
                       
                        end
                      end
                    2. не может быть одна средняя цена. предположим, вы хлотите продать 3 чего-то по цене 1. а в стакане стоят покупки 1 по цене 2, 1 по цене 3 и 1 по цене 4.

                      вы отправляете заявку. средняя цена у вас будет (3 * 1 )/3 = 1, потому, что у вас есть только ваша транзакция. дальше вам приедет сделка 1 по 4, и средняя цена станет (2 * 1 + 1 * 4) / 3 = 2. дальше придет вторая сделка и средняя цена станет (1 * 1 + 1 * 3 + 1* 4) / 3 = 2,666. дальше придет третья сдека и вы получите (0*1 + 1*2 + 1*3 + 1*4)/3 = 3

                      то есть, цена будет уточняться по мере прихода новых сделок по заявке.

                      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
                      36
                      37
                      38
                      39
                      40
                      41
                      42
                      43
                      44
                      45
                      46
                      47
                      48
                      49
                      50
                      51
                      52
                      53
                      54
                      55
                      56
                      57
                      58
                      59
                      60
                      61
                      
                      order_holder = {
                          by_trans_id = {}
                      }
                      setmetatable(order_holder, {
                          __index = {
                              OnOrder = function (self, order)
                                  local holded_order = self[order.order_num]
                                  if holded_order ~= nil then
                                      for k, v in pairs(order) do
                                          holded_order[k] = v
                                      end
                                  else 
                                      self[order.order_num] = order
                                  end
                              end,
                              OnTrade = function (self, trade)
                                  local order = self[trade.order_num] 
                                  if order == nil then 
                                      order = self.by_trans_id[trade.trans_id] or {order_num = trade.order_num}
                                      self[trade.order_num] = order
                                  end
                                  order.trades = order.trades or {}
                                  local trades = order.trades
                                  if trades[trade.trade_num] == nil then
                                     trades.total_qty = (trades.total_qty or 0) + trade.qty
                                     trades.total_vol = (trades.total_vol or 0) + trade.qty * trade.price
                                  end
                                  trades[trade.trade_num] = trade    
                              end,
                              GetMeanPrice = function (self, order_num)
                                  local trades = (self[order_num] or {}).trades or {}
                                  trades.total_qty = trades.total_qty or 0
                                  if trades.total_qty > 0 then
                                      local order = self[order_num] or {qty = 0, price = 0}
                                      local order_balance_qty = math.max(0, order.qty - trades.total_qty)
                                      trades.total_vol = trades.total_vol or 0
                                      return ((order_balance_qty * order.price) + trades.total_vol) / (trades.total_qty + order_balance_qty)
                                  end 
                                  return (self[order_num] or {}).price or 0
                              end
                          }
                      })
                       
                      function OnOrder(order)
                          order_holder:OnOrder(order)
                      end
                       
                      function OnTrade(trade)
                          order_holder:OnTrade(trade)
                          message('Mean expected price for order #'..tostring(trade.order_num)..' is: '..tostring(order_holder:GetMeanPrice(trade.order_num)), 1)
                      end
                       
                      function main()
                          while not exitflag do
                              sleep(10)
                          end
                      end
                       
                      function OnStop() 
                          exitflag = true 
                      end
  7. Здравствуйте, Дмитрий.
    Я новичок и программлю всего чуть больше месяца. Как старший товарищ , пожалуйста, подскажите.
    В скрипте делаю запрос таблицы getParamEx, откуда забираю 10 строк. Пример:

    last = tonumber(getParamEx(Class,Emit,"LAST").param_value)
    BuyDep = tonumber(getParamEx(Class,Emit,"BUYDEPO").param_value)
    SellDep = tonumber(getParamEx(Class,Emit,"SELLDEPO").param_value)
    step = tonumber(getParamEx(Class,Emit,"SEC_PRICE_STEP").param_value)
    и т.д.

    Можно ли как-нибудь упростить запрос? (Возможно я ошибаюсь, но, по-моему к каждой строке идет "индивидуальный запрос", что не есть ГУД!) Что-нибудь как с отправкой транзакций.
    transaction = {
    ["ACCOUNT"]=MyAccount,
    ["CLASSCODE"]=Class,
    ["SECCODE"]=Emit,
    ...
    Например:
    getParamEx(Class,Emit) = {
    tonumber["LAST".param_value]=last,
    tonumber["BUYDEPO".param_value]=BuyDep,
    и.т.д.
    То есть чтобы необходимая таблица "всасывалась за 1 раз".
    И второй вопрос: кроме 10 глобальных переменных param_value есть 2 исключения:

    одно: ..."SHORTNAME").param_image)

    и одно local... - STATUS
    (В крайнем случае с 2 последними исключениями можно смириться ).

    1. 1. в квике нет встроенного способа "упростить запрос". но можно сделать небольшой класс, который упрощает синтаксис, предоставляя доступ к параметрам инструмента как к lua-таблице. делается это примерно так:

      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
      
      params_fetcher = {}
      setmetatable(params_fetcher, {
          __call = function(self, aclass_code, asec_code)
              local tbl = {class_code = aclass_code, sec_code = asec_code}
              setmetatable(tbl, {
                  __index = function(self, key)
                      local res
                      local paramtable = getParamEx(self.class_code, self.sec_code, key)
                      if paramtable ~= nil and paramtable.result == '1' then 
                          if paramtable.param_type == '1' or paramtable.param_type == '2' then
                              res = tonumber(paramtable.param_value)
                          else
                              res = paramtable.param_value
                          end
                      end
      --                if res ~= nil then rawset(self, key, res) end
                      return res
                  end
              })
              return tbl
          end
      })
       
      function main()
          local tmp = params_fetcher('TQBR', 'SBER')
          message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))
      end

      да, все равно, при каждом доступе к параметру tmp.LAST, или любому другому, будет неявно вызываться getParamEx, но можно сделать "кэширование" при помощи rawset(), тогда вызов будет делаться один раз. просто раскомментарьте строку, содержащую rawset(). это полезно, например, если вы внутри одной функции 10 раз обращаетесь к значению LAST и хотите при этом, чтобы доступ к значению был быстрым. меняться это значение не будет.

      2. все эти исключения можно обработать в предлагаемом метаметоде __index(). там уже есть обработка значения поля param_type, можете добавить туда любую логику.

      1. вот так можно улучшить:

        1
        2
        
            __call = function(self, aclass_code, asec_code, acached)
                local tbl = {class_code = aclass_code, sec_code = asec_code, cached = (acached == true)}

        и

        1
        
                        if res ~= nil and self.cached then rawset(self, key, res) end

        , тогда при использовании вот так:

        1
        2
        3
        
            local tmp = params_fetcher('TQBR', 'SBER')
            message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))
            message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))

        функция getParamEx() вызовется ДВА раза, а если так:

        1
        2
        3
        
            local tmp = params_fetcher('TQBR', 'SBER', true)
            message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))
            message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))

        то ОДИН раз. при доступе к параметру LAST во второй раз будет взято значение из кэша.

      2. toxa.
        Премного благодарен, за оперативный и исчерпывающий ответ.
        Только вот проще не стало. В getParamEx размещаются помимо "динамических" (LAST, BID, OFFER) и "статические" данные с уменьшением по степени статичности (SEC_PRICE_STEP, DAYS_TO_MAT_DATE, PRICEMAX, PRICEMIN, BUYDEPO, SELLDEPO). Идея объединения всех getParamEx в одну кучу изначально базировалась на последующей возможного выноса этой таблицы за пределы основной математической функции робота.
        Писать всего робота в одной функции - не хотелось, а распихивать по разным блокам getParamEx получается слишком путано и мудрёно да и наверное не правильно. В настоящий момент в моём скрипте получается, что ресурсы системы неоправданно тратятся на частый запрос статики, а со столь нужной динамикой - тормоза. Созданная "визуализационная" таблица по LAST в сравнении с таблицей "Текущие торги" QUIKa показывает задержку видимую на глаз в районе 0.5-1 сек. Возможно перерисовка тормозит.
        Не пинайте меня сильно это мой первый робот, да и не совсем мой. Попытался стартовый курс обучения QLUA перекрутить на сторону мне необходимую для создания робота. Акелло промахнулся! Признаю.
        Вопросы по предложенному блоку начиная со второго:
        Вы писали ...__index(). там уже есть обработка значения поля param_type... DOUBLE и LONG в вашем случае заменить на param_image? Я пытаюсь по строкам понять что куда дописывать :)))
        1. вопрос:
        Метатаблица params_fetcher, как я понимаю, "собирает" все имеющиеся данные в getParamEx, self- параметр, а ключ - значение?
        В майне ТэЭмПэШка задаёт инструмент, а строковый tmp.LAST и содержит необходимую для меня цену последней сделки?

        Остальные данные с getParamEx мне в Майне tostringИТЬ:
        Задавать типа params_fetcher.SEC_PRICE_STEP = step
        или SPBFUT@SRH0.SEC_PRICE_STEP = step. (это вроде для вывода сообщения!?)

        Еще раз благодарю за помощи в возможно разделении на "динамические" и "статические" данные. Статику действительно можно "послать на ...Камчатку", т.к. нужна 1 - 3 раз за сессию"

    2. там не виден ваш ответ, из-за символа "собака", очевидно, включился режим премодерации. попробую объяснить.

      нет, метатаблица ничего не "собирает". в метатаблице могут быть определены метаметоды, которые вызываются в определенных случаях. так, метаметод __index() вызывается только в том случае, если в таблице нет значения. то есть, если написать a={[5]=10}, то для tmp = a[5] метаметод __index() НЕ БУДЕТ вызван, в для tmp = a[6] будет, так как в а нет ключа 6.

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

      1
      2
      3
      
      tmp.SEC_PRICE_STEP = tmp.SEC_PRICE_STEP
      message(tostring(tmp.SEC_PRICE_STEP))
      message(tostring(tmp.SEC_PRICE_STEP))

      это выглядит диковато, но не лишено смысла. в начале кода tmp не содержит пару 'SEC_PRICE_STEP': , это приводит к вызову метаметода __index(), получению параметра SEC_PRICE_STEP при помощи getParamEx(). дальше, в левой части первой строки мы добавляем ключ 'SEC_PRICE_STEP': . и на второй и третьей строке при получении значения по ключу SEC_PRICE_STEP метаметод __index() не вызывается, а просто возвращается имеющееся значение.

      что касается типов параметров. функция getParamEx() возвращает таблицу со строками: "результат вызова", "тип параметра", "значение", "значение в читаемом виде". в метаметоде мы смотрим "результат вызова", если он "успех", то смотрим "тип параметра", если он "целое число" или "дробное число", то мы возвращаем "значение" приведенное к числовому типу LUA. иначе мы возвращаем строку. мне было лень для примера писать обработку ВСЕХ случаев, например, преобразование даты/времени из строки в unixtime, для строковых параметров брать не "значение", а "значение в читаемом виде" и прочее, о чем вы пишите.

      таким образом tmp.LAST вернет нам NUMBER, tmp.SHORTNAME строку STRING, а tmp.ERRORPARAM вернет nil, так как параметра ERRORPARAM не существует и "результат вызова" = "ошибка". преобразовывать типы внутри лучше, так как это повышает читаемость кода. лучше написать tmp.BID - tmp.OFFER чем tonumber(getPatamEx('TQBR', 'SBER', 'BID').param_value) - tonumber(getPatamEx('TQBR', 'SBER', 'OFFER').param_value)

      1. собака SRH0.SEC_PRICE_STEP = step. (это вроде для вывода сообщения!?)

        Еще раз благодарю за помощи в возможно разделении на "динамические" и "статические" данные. Статику действительно можно "послать на ...Камчатку", т.к. нужна 1 - 3 раз за сессию"

        1. Ещё раз благодарю. Робот РИСКОпил вроде работает (убытки пилит) :))) С Вашим блоком замены toxa, я обязательно в течении ближайших пары дней разберусь. Если появятся вопросы можно будет обратиться?

          1. вот окончательный вариант. что непонятно - пишите.

            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
            36
            37
            38
            39
            40
            41
            42
            43
            44
            45
            46
            47
            
            params_fetcher = {}
            setmetatable(params_fetcher, {
                __call = function(self, aclass_code, asec_code, acached)
                    local tbl = {class_code = aclass_code, sec_code = asec_code, cached = (acached == true)}
                    setmetatable(tbl, {
                        __index = function(self, key)
                            local res
                            local paramtable = getParamEx(self.class_code, self.sec_code, key)
                            if paramtable ~= nil and paramtable.result == '1' then 
                                if paramtable.param_type == '1' or paramtable.param_type == '2' then
                                    res = tonumber(paramtable.param_value)
                                else
                                    res = paramtable.param_image
                                end
                            end
                            if res ~= nil and self.cached then rawset(self, key, res) end
                            return res
                        end
                    })
                    return tbl
                end
            })
             
            sec_params = {}
            setmetatable(sec_params, {
                __index = function(self, class_code)
                    local codes = {}
                    setmetatable(codes, {
                        __index  = function(self, sec_code)
                          local sec_table = params_fetcher(class_code, sec_code)
                          rawset(self, sec_code, sec_table)
                          return sec_table
                        end
                    })
                    rawset(self, class_code, codes)
                    return codes
                end
            })
             
            function main()
                -- example 1:
                message('TQBR@SBER.LAST == ' .. tostring(sec_params.TQBR.SBER.LAST))
                -- example 2:
                local tqbr_sber = sec_params.TQBR.SBER
                tqbr_sber.cached = true -- enable parameter caching, getParamEx() is called only once per parameter
                message('TQBR@SBER.SHORTNAME == ' .. tostring(tqbr_sber.SHORTNAME))
            end
            1. Здравствуйте, toxa.
              Ещё раз благодарен за "окончательный вариант". На "стоячем рынке" сбер. фьюч "забирает прекрасно (появилось время опробовать, но с 22 по 24 февраля "динамики" по торгам нет :-))). Праздники.
              Вопрос по Вашим примерам. В первом примере значение - param_value (tonumber). Во втором - param_image (tostring) и, как я понял, оба подсасываются с кеша, судя по наличию rawset в описанном вами в первом посте. Цитирую: "... но можно сделать "кэширование" при помощи rawset(), тогда вызов будет делаться один раз. ...".
              Как я понял в первой функции получение данных.
              Вопросы:
              sec_params нужна для кеширования?
              И если я закомментирую в первом блоке условие ...тогда

              1
              
               then rawset(self, key, res)

              , будут ли данные поступать динамично?

              1. нет. в example1 кэширования не используется. сколько раз вы вызовите sec_params.TQBR.SBER.LAST (и любые другие параметры), столько раз будет вызван to_number(getParamEx('TQBR', 'SBER', 'LAST').param_value).

                в example2 включается кэш, и первый вызов будет проброшен в getParamEx(), резудльтат будет записан в таблицу, а второй и последующий не будут приводить к вызову getParamEx(), а результат будет сразу браться из таблицы. это связано с особенностями вызова метаметодов. дело в том, что метаметод __index для таблицы будет вызываться только в том случае, если в этой таблице отсутствует ключ. ключом у нас выступает имя параметра. предположим, мы запросили SHORTNAME, такого ключа в таблице нет, вызвалась функция getParamEx('TQBR', 'SBER', 'SHORTNAME'), далее при помощи rawset() в таблицу добавилась пара SHORTNAME = 'Сбербанк'. в следующий раз вызова __index не произойдет, и нам LUA сразу отдаст 'Сбербанк' при запросе ключа SHORTNAME.

                вставьте после "local paramtable = getParamEx(self.class_code, self.sec_code, key)" следующей строкой что-нибудь типа message('GetParamEx called', 1); и увидите, когда эта функция реально вызывается, а когда - нет.