Пример скрипта QLua (Lua), выполняющего торговые операции в терминале QUIK

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

OpenClose
Скрипт позволяет открывать и закрывать позиции следующего типа:
- покупается 2, выбранных при помощи кнопок, колл-опциона на фьючерс на индекс РТС,
- продается 1, выбранный при помощи кнопок, фьючерс на индекс РТС.

Когда есть открытая данным скриптом позиция, в таблице отображается информация о ней (профит, баланс, ср.цена, дата открытия).

Профит вычисляется по формуле: "Текущая цена, по которой можно закрыть позицию" - "Цена открытия позиции" - "Комиссия 8 р. на каждый лот".

Так же, выполняется запись в лог-файл ("Log.txt") выполняемых скриптом операций и в файле состояния ("State.txt") хранится информация о текущих выбранных инструментах и о текущей, открытой скриптом позиции.

ВАЖНО!!! При выборе опциона, кнопками "/\","\/" меняется страйк опциона с шагом 500, убедитесь в том, что такой опцион существует!!!

Код скрипта

СКАЧАТЬ ДАННЫЙ СКРИПТ

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

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

Пример скрипта QLua (Lua), выполняющего торговые операции в терминале QUIK: 110 комментариев

  1. Дмитрий, добрый день.
    На примере данного скрипта, выгружаю данные по открытым сделкам. Сделал так же, каждое значение на своей строке: объем и следом цена. Пытаюсь загрузить и вставить в таблицу, но выдает что поле nil.
    Вот код:

    1
    2
    3
    4
    5
    
    elem = data[buy]
    elem = {}
    if Count == 3 then elem.qty = tonumber(line) -- объем
    elseif Count == 4 then elem.price = tonumber(line) -- цена
    end

    Если вывести через message, то цену показывает, а на поле объема elem.qty ругается nil, хотя в файле стоит 1 на этой строке.
    Если я пишу так:

    1
    2
    3
    4
    5
    
    elem = data[buy]
    elem = {}
    if Count == 3 then vol = tonumber(line) -- объем
    elseif Count == 4 then elem.price = tonumber(line) -- цена
    end

    то выдает оба значения без проблем. В чем может быть косяк?

    1. Разобрался, условие ж написал либо/либо... Вот и получал один вариант. Заменил elseif на if и все пошло... Заработался уже, пора в отпуск 🙂
      Спасибо вам за сайт, полезного просто море)))

      1. Да, я уже понял, что рано обрадовался))) Дальше не пошло, сижу разбираюсь))) Там массив я до прохода по строкам файла ставлю. Весь код ниже.
        У меня есть двумерный массив data, в нем ключ buy и поля qty и price. Сделал файл и добавил 2 строки, пытаюсь их считать и вроде все ок, но что-то идет не так, потому что я в другом месте пытаюсь эти данные обработать и тишина.

        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
        
        data = {}
        Run = true
         
        function OnInit()
        -- Пытается открыть файл состояния в режиме "чтения/записи"
        local f = io.open(getScriptPath().."//State2.txt","r+")
        -- Если файл существует
        if f ~= nil then 
        	local Count = 0 -- Счетчик строк
        	elem = data[buy]
        	elem = {}
        	for line in f:lines() do 
        		Count = Count + 1
        		if Count == 1 then elem.Pos = tonumber(line)
        		elseif Count == 2 then elem.Avr = tonumber(line)
        		elseif Count == 3 then break
        		end
        	end
        	tolog(elem.Pos) -- вижу в логе цифру
        	tolog(elem.Avr) -- вижу в логе цифру
        end
        -- Закрывает файл
        f:close()
        end
         
        function main()
        while Run do sleep(1) end
        end
         
        function OnParam(class, sec)
        	if class == "SPBFUT" and sec == "RIU6" then
        	message("Изменения") -- вижу
        	elem = data[buy]
        		if elem ~= nil then
        tolog("OnParam() "..elem.Pos) -- уже не приходит в лог
        tolog("OnParam() "..elem.Avr) -- уже не приходит в лог
        message("массив не пустой") -- не вижу
        		end
        	end
        end

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

          1. Я наверное неправильно синтаксис понимаю 🙁 Можно ли присваивать значение массива data[buy] любому массиву? Т.е. в начале я занес его в массив с именем elem, а потом можно ли занести эти же данные в массив elem55, к примеру?

            1. Можно, только до этого Вы присваивали data[buy], которого еще не существовало, в последнем коде уже более правильно, но есть лишние строки.
              Зачем писать так:

              1
              2
              3
              
              data = {}
              elem = "buy"
              data[elem] = {}

              если можно сразу так:

              1
              2
              
              data = {}
              data["buy"] = {}

              или даже так:

              1
              2
              
              data = {}
              data.buy = {}
      2. Разобрался, ниже код с исправлениями. Дмитрий, можете подсказать, в чем разница? Почему не считывался предыдущий пример массива? У вас опыта больше, а я в прострации)))

        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
        
        data = {}
        Run = true
         
        function OnInit()
        -- Пытается открыть файл состояния в режиме "чтения/записи"
        local f = io.open(getScriptPath().."//State2.txt","r+")
        -- Если файл существует
        if f ~= nil then 
        	local Count = 0 -- Счетчик строк
        	elem = "buy" -- изменил тут
        	data[elem] = {} -- изменил тут
        	for line in f:lines() do 
        		Count = Count + 1
        		if Count == 1 then data[elem].Pos = tonumber(line) -- изменил тут
        		elseif Count == 2 then data[elem].Avr = tonumber(line) -- изменил тут
        		elseif Count == 3 then break
        		end
        	end
         
        	tolog(data[elem].Pos) -- вижу в логе
        	tolog(data[elem].Avr) -- вижу в логе
        end
        -- Закрывает файл
        f:close()
        end
         
        function main()
        while Run do sleep(1) end
        end
         
        function OnParam(class, sec)
        	if class == "SPBFUT" and sec == "RIU6" then
        	message("Изменения")
        	elem = "buy" -- изменил тут
        		if data[elem] ~= nil then -- изменил тут
        tolog("OnParam() "..data[elem].Pos) -- появилось в логе
        tolog("OnParam() "..data[elem].Avr) -- появилось в логе
        message("массив не пустой") -- увидел
        		end
        	end
        end
  2. Доброго дня Дмитрий, открыл код Вашего робота, поменял Account и другие требуемые позиции.
    Но Квик после загрузки робота и нажатия кнопки "Купить" - выдает сообщения о несуществующем опционе с другими индексами, а именно: после нажатия кнопки купить в Вашем роботе выходит окно из Квика - "Покупка опционов не удалась ! Ошибка : указанный инструмент не найден RI091500BF6". Хотя - я в коде Вашего робота поменял название на нужный мне опцион - RI92500BH6.
    Вот ниже данные, что я вписал в код робота >
    CLASS_CODE_OPT = "SPBOPT"; -- Класс ОПЦИОНОВ
    CLASS_CODE_FUT = "SPBFUT"; -- Класс ФЬЮЧЕРСОВ
    SEC_CODE_FUT_FOR_OPEN = "RIU6"; -- Код ФЬЮЧЕРСА для открытия
    SEC_CODE_OPT_FOR_OPEN = "RI92500BH6"; -- Код ОПЦИОНА для открытия
    SEC_CODE_OPT_IN_POS = "RI92500BH6"; -- Код ОПЦИОНА в позиции
    OPEN_BALANCE_OPT = 0; -- Баланс позиции по ОПЦИОНАМ
    OPEN_PRICE_OPT = 1040; -- Средняя цена открытия позиции по ОПЦИОНАМ
    OPEN_DATE_OPT = "04.08.2016"; -- Дата открытия позиции по ОПЦИОНАМ
    SEC_CODE_FUT_IN_POS = "SPBFUT"; -- Код ФЬЮЧЕРСА в позиции
    OPEN_BALANCE_FUT = 0; -- Баланс позиции по ФЬЮЧЕРСАМ
    OPEN_PRICE_FUT = 93190; -- Средняя цена открытия позиции по ФЬЮЧЕРСАМ
    OPEN_DATE_FUT = "04.08.2016"; -- Дата открытия позиции по ФЬЮЧЕРСАМ
    Что можно настроить ?
    Заранее благодарен.

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

      1. Доброго времени суток Дмитрий, по этому скрипту переписывался с Константином - но как оказалось не его продукт.
        Менял я настройки этого робота, но все время выходит один и тот же не существующий опцион.
        Я не программист.
        Прошу поддержки в настройке этого робота - если возможно, то платно через программу Team Weaver ?

  3. Кажется функция "function OnStop()" перегружена кодом.
    Там уместно только "IsRun = false;"
    Дело в том, что цикл в "main" может прерываться не только штатно. А, например, по закрытию окна или по достижению ограничения убытков.
    "IsRun = false;" в общем случае может появиться в любой функции проверки, а завершение нужно отработать корректно и полно.
    Словом, правильней весь код финша скрипта лучше перенести в main за бесконечный цикл. Может в виде функции UnInit() 🙂

    PS. команда: "Log:close();", если не отработает, то повесит файл открытым и недоступным со стороны ОС. Кажется, по окончанию пользовательского скрипта qlua не производит утилизацию открытых хандлов.

    1. Я, вообще OnStop(), сам вызываю в том месте скрипта, где нужно остановить скрипт. Возможно, как Вы пишите, будет разумнее сделать, но в данном скрипте задача - записать в файл 10 коротких строчек и закрыть его, это, я считаю допустимо. Главное в OnStop() не делать долгих вычислений, то скрипт может завершиться раньше, чем эта функция до конца отработает.

      1. Вы абсолютно правы относительно данного скрипта.
        Я только хотел отметить имеющиеся трудности масштабирования скрипта, если не знать его особенности. Например, если main будет организован так:
        while not IsWindowClosed(t_id) and IsRun do
        То как вызвать OnStop()?
        ------------
        Не могли бы ответить. Как понимаю, кнопка меню квика "остановить" скрипт может остановить зависший скрипт принудительно, перед этим вызывая OnStop(). Каков таймаут (время ожидания самозавершения скрипта)?

        1. В Вашем примере нужно в обработчике клика по крестику окна вызывать OnStop(), скорее всего.
          По кнопке "остановить", как я понимаю, алгоритм следующий:
          1.Нажимаете "остановить"
          2.Вызывается (терминалом) функция OnStop()
          3.Выполняется код из функции
          4.Если main еще выполняется, то через 5 сек она принудительно завершается, изменить этот таймаут можно вернув из функции OnStop() другое число. Например, если вернуть return 3000, то терминал принудительно завершит main через 3 секунды, а не через 5(по умолчанию). Но если Вы в OnStop() сбрасываете флаг цикла (run = false), то к моменту выполнения OnStop() цикл в main уже и сам может остановиться.

          1. Я правильно понял:
            1. Таймер принудительного завершения можно только уменьшить от 5 сек?
            2. OnStop() работает в отдельном потоке? раз main может завершиться до окончания OnStop() .

              1. Выходит, что main может завершиться как до окончания OnStop() , так и после. (Если конечно он вызван не из самого скрипта). Следовательно, если main продолжит работу хотя бы полцикла, то OnStop() зафиксирует не конечные параметры (скажем после него будет проведена сделка).
                Понимаю так, что кнопкой из меню квика "остановить" скрипт лучше пользоваться в аварийном случае, а останавливать скрипт самому по своей кнопке или закрытию окна.

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

    1. "Цена заявки, за единицу инструмента. Обязательный параметр. При выставлении рыночной заявки (TYPE=M) на Срочном рынке FORTS необходимо указывать значение цены – укажите наихудшую (минимально или максимально возможную – в зависимости от направленности), заявка все равно будет исполнена по рыночной цене. Для других рынков при выставлении рыночной заявки укажите price= 0"
      (Руководство пользователя QUIK(info.chm) -> Раздел 6. Совместная работа с другими приложениями -> Импорт транзакций -> Формат .tri-файла с параметрами транзакций (описание параметра PRICE))

  5. Здравствуйте, Дмитрий!
    Выставляю скриптом лимитную заявку. В таблице транзакций есть ответ: Транзакция выполнена. Заявка, с биржевым номером 18576305521, успешно зарегистрирована.,
    но ни в таблице заявок ни в позициях по счетам этой заявки нет.

    1. Здравствуйте, Андрей!
      Попробуйте правой кнопкой мыши в таблице заявок -> "Редактировать таблицу", а там выберите по очереди каждый класс из списка "Выбранные классы" и убедитесь, что у Вас так же стоят галочки для каждого класса:
      Изображение:

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

  6. Шикарный пример робота (я только начал изучать Lua, раньше на МТ4 форекс роботов писал), побольше примеров для ОПЦИОНОВ - пожелание. Спасибо за данный пример. Шикарный сайт.

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

  8. Добрый время суток я понимаю конструкция собирается по рынку! А можно сделать так что бы она собиралась на 10-20 хуже теоретической цены?