Нужные функции

Здесь буду выкладывать функции, которые могут пригодиться:

math_round() -- Округляет число до указанной точности
GetClassBySec() -- Возвращает код класса по коду бумаги
WaitUpdateDataAfterReconnect() -- Ждет подключения к серверу, после чего ждет еще UpdateDataSecQty секунд подгрузки пропущенных данных с сервера
GetServerDateTime() -- Возвращает текущую дату/время сервера в виде таблицы datetime
StrToTime() -- Приводит время из строкового формата ЧЧ:ММ к формату datetime
CheckDemo() -- Узнать является ли терминал демо от компании Arqa, или демо брокера Открытие
GetTotalnet() -- Получает текущую чистую позицию по инструменту
GetFreeMoney() -- Возвращает доступные средства
GetCorrectPrice() -- Приводит переданную цену к требуемому для транзакции по инструменту виду
GetPriceForMarketOrder() -- Возвращает корректную цену для рыночной заявки по текущему инструменту
SetOrder() -- Выставляет обычную лимитную заявку
SetMarketOrder() -- Выставляет рыночную (по сути) заявку
CheckOrder() -- Проверяет наличие в системе заявки с определенным ID транзакции
GetOrderNum() -- Возвращает номер заявки по ее ID транзакции
WaitOrderComplete () -- Ожидает исполнения заявки по ID транзакции
Set_SL() -- Выставляет 'Стоп лимит' заявку
SetTP() -- Выставляет 'Тейк профит' заявку
SetTP_SL() -- Выставляет 'Тейк профит и Стоп лимит' заявку
CheckStopOrder() -- Проверяет наличие в системе стоп-заявки с определенным ID транзакции
GetStopOrderNum() -- Возвращает номер стоп-заявки по ее ID транзакции
CheckStopOrderActive() -- Проверяет по номеру активна ли стоп-заявка
KillOrder() -- Снимает заявку
Kill_SO() -- Снимает стоп-заявку
StackFIFO() -- Создает объект стека FIFO(Первым вошел, первым вышел)

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

Нужные функции: 114 комментариев

  1. Может кому пригодится.
    Если вам нужно распарсить строку сообщения квик, то там могут находится символы, которые визуально смотрятся как пробел, в частности char(160) используется в ценах, например 63 800. Обычным string.gsub("%s", "") такой "пробел" не убрать. Пользуйтесь:
    local function ConvertPrice(text)
    local s = text:gsub("%s+", "")
    s = s:gsub('.',
    function (s)
    local sb = s:byte()
    return (sb == 160 and "") or (sb == 44 and ".") or s
    end)
    return tonumber(s)
    end

  2. Добрый вечер Дмитрий,сразу о наболевшем ) при переборе таблиц циклом индек i надо уменьшать на единицу ) так как начало с нуля ,------и вопрос при отправке транзакции потом получениее ёё номера sleep(1000) и снятия sleep(1000) только при такой задержкевсе коректно работает можно както сократить задержку и с чем это связано,?

    1. Здравствуйте, я после отправки транзакции, например, на выставление заявки, перебираю таблицу заявок и жду пока в ней появится новая с тем же trans_id. Таким образом никаких лишних пауз не добавляется.

          1. Да, лимит времени это гуд, но не панацея, я сделал дополнительную тестовую заявку "покупка" по pricemin, которую отправляю через лимит времени, получаю по ней ответ...
            Отлично! Значит все гуд и целевая заявка потерялась, можно ее повторить.
            В итоге получаю две заявки, первая потеряшка чудесным образом нашлась!
            И это нормально!
            Сеть выбирает наиболее не загруженный канал, но это не гарантирует, что в момент движения пакета, этот канал резко не загрузился каким-то архивом в пару экзобайт, а для последующих заявок сеть выберет другой маршрут.
            Отсюда вывод, запоминать ранее отправленную заявку, отправлять новую, и если старая вдруг нарисовалась удалить ее или сразу закрыть открытую ей позицию. Как я не люблю эти танцы с бубном.

  3. Здравствуйте, изучал функции время. Вроде все понял и получилось его получить. Но пока не пойму как это использовать. Не нашел информации. Например для работы скрипта от и до заданного времени. Или точнее для выполнения функции main.

    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
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      
      END_DAY                    = '18:40'               -- Время завершения работы робота
      RUN                        = true
      ServerDT                   = nil
      ServerDT_sec               = 0
      EndDay_sec                 = 0
       
      function main()
         -- Получает время завершения в секундах
         EndDay_sec = os.time(StrToTime(END_DAY))
       
         -- Основной цикл
         while RUN do
            -- Получает дату/время сервера
            ServerDT = GetServerDateTime()
            -- Переводит в секунды
            ServerDT_sec = os.time(ServerDT)
            -- Если день закончился
            if ServerDT_sec >= EndDay_sec then
               -- Завершает работу робота
               OnStop()
               return
       
            -- Иначе, выполняет основной алгоритм
            else
               Algo()
            end
       
            sleep(10)
         end   
      end
       
      function OnStop()
         RUN = false
      end
       
      -- Основной алгоритм
      Algo = function()
         -- Что-то делает
      end
       
      -- Приводит время из строкового формата ЧЧ:ММ к формату datetime
      StrToTime = function(str_time)
         if type(str_time) ~= 'string' then return os.date('*t') end
         local sdt = GetServerDateTime()
         while RUN and sdt.day == nil do sleep(100) sdt = GetServerDateTime() end
         if not RUN then return os.date('*t') end
         local dt = sdt
         local h,m = string.match( str_time, "(%d%d):(%d%d)")
         dt.hour = tonumber(h)
         dt.min = tonumber(m)
         dt.sec = 0
         return dt
      end
      -- Возвращает текущую дату/время сервера в виде таблицы datetime
      GetServerDateTime = function()
         local dt = {}
         while RUN and dt.day == nil do
            dt.day,dt.month,dt.year,dt.hour,dt.min,dt.sec = string.match(getInfoParam('TRADEDATE')..' '..getInfoParam('SERVERTIME'),"(%d*).(%d*).(%d*) (%d*):(%d*):(%d*)")
            for key,value in pairs(dt) do dt[key] = tonumber(value) end
            if dt.day ~= nil then return dt end
            sleep(100)
         end
         return os.date('*t')
      end
      1. Спасибо. Все наладил, с условием от и до. Не понимал как именно сравнивать. Выставлять условие из чего. Какие брать значения. Из примера все стало понятно. Немного затянул с ответом, думал еще будут вопросы)

  4. Дмитрий здравствуйте!
    Вопрос, можно ли использовать следующий код для получение объеиа по инструменту? Если нет, то почему.
    LotSize=0.0;
    for i = 0,getNumberOf('depo_limits') - 1 do
    local depo_limit = getItem("depo_limits", i)
    if depo_limit.sec_code==SEC_CODE and depo_limit.currentbal>LotSize then LotSize=depo_limit.currentbal end
    end

  5. Дмитрий здравствуйте.

    У Вас случайно не было ли такого что depo_limits.awg_position_price возвращала бы цену деленную на 100 ?
    У меня на демо тесте вылетает такая ошибка, на реале нету возможности испытать это. Задал вопрос Арки, но они уже пол недели молчат...
    т.е. допустим открылся по цене 1 000, а depo_limits. awg_position_price = 10.

      1. Благодарю Вас за ответ.

        Вполне возможно, что это ошибка моей версии терминала (судя по ответу техподдержки) на всякий случай скину сюда ссылку на вопрос, вдруг местным завсегдатым будет полезно (если еще кто нибудь наткнется на подобное) (https://forum.quik.ru/forum10/topic3445/)

  6. Дмитрий здравствуйте,
    У меня вопрос по выставлению стопа (делаю функцию сейчас которая отдельно стоп и отдельно тейк выставляла бы)
    Я запутался в заполнении таблицы. а именно с полями

    STOPPRICE - по чему у Вас при стоп лоссе на покупку там минимальная цена фьюча используется, а в тейке наоборот

    MARKET_STOP_LIMIT и MARKET_TAKE_PROFIT - почему у Вас они отмечены как Не рыночные ? стопы и тейки же для ограничения убытка по рынку бить должны на сколько я их воспринемаю (или же у Вас делается жмуляция рыночной заявки для фьючей если да, то будет ли этот же метод работать на акциях ?)

    PRICE - опять же немого мудренно для меня, по чему она опять как то высчитывается из цен STOPPRICE ?

    Я для себя хочу сделать просто функцию, в которую заношу цену активации, а далее стоп (или тейк) срабатывают как обычный стоп или же тейк, для ограничения убытка по позиции. Заполнил следующие поля (и описал свои условия срабатывания) : TRANS_ID, CLASSCODE, SEC_CODE, ACCOUNT, ACTION, QUANTITY, OPERATION, STOP_ORDER_KIND.
    Причем последнее поле для стопа оставил без внимания, а для тейка - поставил как "TAKE_PROFIT_STOP_ORDER"

    Мне нужно еще описанные поля заполнить как я понимаю ( PRICE, STOPPRICE, MARKET_STOP_LIMIT и MARKET_TAKE_PROFIT ) и вот они то как раз и вызвали затруднение... надеюсь на Вашу помощь) Благодарю заранее)

    1. Здравствуйте! Я обычно эмулирую рыночную заявку, т.е. рыночных заявок вообще не существует, по идее, рыночная заявка это просто заявка по очень "плохой" цене, которая при выставлении в стакан исполнится по первой встречной заявке. Они там что-то меняют постоянно, раньше по фьючам нельзя было рыночную заявку отправлять, сейчас можно стало, по этому я использую универсальный подход. По поводу PRICE, это цена заявки (реальной), которая выставится в стакан при достижении цены стопа условия STOPPRICE для стопа и STOPPRICE2 для тейкпрофит-стоп. Так как я ее тоже условно рыночной делаю, то проще отступить сколько-то от стоп-цены. А вообще, самый простой способ разобраться, это повыставлять программно разные заявки на демке и посмотреть как они исполняются. Значения параметров с примерами есть в файле справки info.chm, там раздел что-то типа "Работа с другими приложениями", "Импорт транзакций через API", "Параметры транзакций", "Примеры строк"

      1. То есть получается следующая схема:

        Допустим мне нужно поставить стоп для лонга (или тейк для шорта) по цене 150.

        SL:
        1) Ставится цена активации - STOPPRICE = 150
        2) При достижении этой цены 150 ставится PRICE = 100 и далее робот рубит по рынку пока не проталкнет весь объем или же не дойдет до 100

        TP:
        1) Ставится цена активации - STOPPRICE = 150
        2) При достижении этой цены 150 ставится PRICE = 200 и далее робот рубит по рынку пока не проталкнет весь объем или же не дойдет до 200

        Я верно схему понял ? За ссылки благодарю, по изучаю завтра как торги начнутся.

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

  7. Дмитрий здравствуйте.
    А почему в примерах функций практически везде есть блок который в цикле дожидается результата ? (при подписки на котировки к примеру, или же при удалении стоп ордеров) ?
    Разве это не синхронный запрос ? На сколько я понимаю, этот код выполняется в контексте одного потока и по этому следующая строчка кода не должна выполняться пока не будет выполнено действие (удаление ордера к примеру или же готовая структура с подпиской на котировки. )

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

      1. Тогда уж проще писать на QPILE там уже все это есть. Если робот торгует сотней инструментов одновременно, то такой подход не пойдет. Все ваши функции не верны априори.

          1. Вне зависимости от задач, функции должны выполняться максимально эффективно.
            Сейчас задача торговать одним инструментом, завтра - сотней и что? Переделывать все функции?
            Зачем в datetime менять string на number? Не за чем.
            Вот функция, которая заменяет 2 ваши функции по преобразованию цены, да и просто округляет все что нужно, в разумных пределах, например время(posix) до нужного таймфрейма, до H1 включительно.

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            
            function floor(v, s, up)
              if up then
                return math.floor(v/s+0.5)*s
              else
                return math.floor(v/s)*s
              end
            end
            --округленные цены смело вставляем в заявку, через tostring
            sec_price_step = 0.005
            print(floor(0.277556, sec_price_step))             -->0.275
            print(floor(0.277556, sec_price_step, true)) -->0.28
            --12-ти секундный таймфрейм
            print(os.date("%Y.%m.%d %H:%M:%S", floor(os.time(),12)))

            И никаких танцев с бубном, мало того, эта функция в 14 раз быстрее, чем math_round, который по привычке пользует 99% народу.
            Медленно - это ключевое слово, для описания всех ваших "полезных" функций, особенно CheckOrder - жесть просто.
            Ну ладно пару сотен записей в таблице заявок, а ну как 5-10 тыс и это минут за 20, что будет делать эта функция? Правильно, заниматься хер..й.
            Без обид.

            1. string на number потому что дата это числовой тип, а не строковый, по моему все логично, наоборот непонятно для чего работать с датой как со строкой 🙂 99,9% клиентов заказывают роботов, которые торгуют одним инструментом и никаких проблем нет с этими функциями. Ты со своей колокольни судишь, у тебя задачи другие и они тебе не подходят, а я всех роботов, за редким исключением, писал раньше, используя эти функции с минимальной кастомизацией и все довольны 🙂 И чем твоя floor быстрее math_round ? И там и там внутри math.floor.
              Никаких обид, естественно 🙂

              1. Странно, что-то в квике нет даты числового типа. Функции преобразования типов медленные. Если уж и получать дату в виде числа, то так:
                yyyymmdd = tonumber(os.date("%Y%m%d, os.time())) -- tonumber вызван всего 1 раз.
                Плюс там в цикле вы меняете булевый флаг(isdst) дневного времени суток на nil, что при его использовании приведет к логической! трудно находимой ошибке.
                А как по мне - posix надежнее, округляется как мне нужно, ну не суть.
                Как работают os.time и os.date и что с ними можно и нужно делать: http://www.bot4sale.ru/blog-menu/qlua/368-lua-time.html

                На счет минимальной кастомизации, сильно прям сомневаюсь.
                А отсутствие проблем с "нужными" функциями соблюдается при единственном условии - 99,9% клиентов через неделю торгов на реальные деньги удалили робота, использующего эти функции и пошли устраиваться на работу, как то так.

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

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

                1. Мы получаем дату/время сервера в функции, а не компа, так что os.date с tonumber нам не помощник 🙂 А по поводу работы роботов никто из клиентов ещё не жаловался. По поводу долгого возведения в степень, мне кажется ты путаешь Квик с прямым подключением, здесь у нас HFT не пишут, где критичны микросекунды, здесь задачи тривиальные решаются, а лучшее, как известно, враг хорошего 🙂

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

                  1. Отнюдь) я ни чего не путаю, разницы нет, какую дату/время получать, сервера или компа.
                    А по поводу работы роботов никто жаловаться и не будет - чего жаловаться, если стратегия не работает.
                    По поводу HFT готов поспорить, дело не в самой скорости, у меня есть роботы, которые несколько раз в день включаются и чего-то там быстро считают. И тут ключевое слово - быстро, т.к. мне важно не загружать проц бесполезными расчетами.
                    И да код на Lua нужно писать таким образом, что бы была возможность его быстро портировать на С.

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

                  2. Суть задачи это научить, согласен. Я помню как я начинал писать, голова дымилась это точно.
                    Но если бы мне тогда дали мой смарт, дыму бы не убавилось это точно, но года 3-4 я бы сэкономил, и лишь это! важно.
                    Ты же предлагаешь всем пройти путь, который мы прошли сами (минус 3-4 года жизни), а это не правильно.
                    С таким подходом можно было дать ребенку азбуку, объяснить, что из букв можно составить слова, а из слов предложения, а дальше пусть сам додумывает.
                    Т.е. используя твой метод обучения, мы бы еще жили в каменном веке и вообще бы не парились с роботами.

                    1. Естественно, я хочу навязать свой подход, стандарты это хорошо, без них далеко не уедешь.

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

                    3. Если хочешь внести свой вклад, то напиши статью, в которой донеси в понятной для широкой общественности форме свои подходы, то, к чему ты пришел за эти годы.

                    4. Возможно, наберу пива по-больше и напишу, а может будет лень, тогда увы и ах)