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

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

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

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

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

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

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

Код скрипта

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

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

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

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

  1. Подускажите, а можно переделать скрипт, чтобы можно было набирать позицию больше чем 2 опциона+1 фьюч. Что необходимо изменить? Заранее благодарен.

    1. Здравствуйте! Давно писал данный скрипт, не помню уже в подробностях как именно он работает, но пробежавшись взглядом предполагаю, что будет достаточно изменить значения в следующих строках на нужные:
      191
      220
      256
      285
      305
      306 - в этой строке меняйте только цифру, знак минус оставьте, потому что когда шорт, то баланс со знаком минус

    1. Здравствуйте. В функции CreateTable скрипта нужно добавить еще одну колонку для вывода волатильности.
      В функции OnParam при помощи getParamEx получать значение волатильности (поле VOLATILITY) по выбранному опциону и выводить значение в таблицу при помощи функции SetCell.

  2. Здравствуйте.
    Подскажите, возможно ли через файл state.txt вручную подключить уже открытые позиции фьючерс/опцион? Просьба выложить на любой файлообменник записанный файл state.txt .

    1. Здравствуйте! Я уже сейчас не помню в каком формате он записывает данный файл, поступите проще: запустите данный скрипт на демо-счете, откройте позицию и остановите скрипт, потом откройте данный файл и поправьте там коды, количество и даты открытия. Формат, на самом деле, простой, каждое новое значение в новой строке, а вот такой общий список строк:

      Код ФЬЮЧЕРСА для открытия
      Код ОПЦИОНА для открытия
      Код ОПЦИОНА в позиции
      Количество лотов в позиции по ОПЦИОНАМ
      Средняя цена открытия позиции по ОПЦИОНАМ
      Дата открытия позиции по ОПЦИОНАМ
      Код ФЬЮЧЕРСА в позиции
      Количество лотов в позиции по ФЬЮЧЕРСАМ
      Средняя цена открытия позиции по ФЬЮЧЕРСАМ
      Дата открытия позиции по ФЬЮЧЕРСАМ

  3. Здравствуйте! Подскажите, пожалуйста, как сделать так, чтобы при закрытии сделки стоп к ней не снимался.
    Пример:
    купил 1 лот сбера и поставил стоп и тейк
    потом продал 1лот сбера стоп и тейк автоматически снялись.
    Как сделать чтобы не снимались.

      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
        
         function sendMarket(class,security,direction,volume,account,client_code,comment)
        	-- отправка рыночной заявки
        	-- Данная функция возвращает 2 параметра
        	--     1. ID присвоенный транзакции либо nil если транзакция отвергнута на уровне сервера Квик
        	--     2. Ответное сообщение сервера Квик либо строку с параметрами транзакции
        	if (class==nil or security==nil or direction==nil  or volume==nil or account==nil) then
        		return nil,"sendMarket Nil parameters."
        	end
         
        	local trans_id=random_max()
        	local transaction={
        		["TRANS_ID"]=tostring(trans_id),
        		["ACTION"]="NEW_ORDER",
        		["CLASSCODE"]=class,
        		["SECCODE"]=security,
        		["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)~=nil then
        		local sign=0
        		if direction=="B" then
        			transaction.price=getParamEx(class,security,"pricemax").param_value
        			if transaction.price==0 then
        				transaction.price=getParamEx(class,security,"offer").param_value+10*getParamEx(class,security,"SEC_PRICE_STEP").param_value
        			end
        			sign=1
        		else
        			transaction.price=getParamEx(class,security,"pricemin").param_value
        			if transaction.price==0 then
        				transaction.price=getParamEx(class,security,"bid").param_value-10*getParamEx(class,security,"SEC_PRICE_STEP").param_value
        			end
        			sign=-1
        		end
        		-- last chance
        		if transaction.price==0 then
        			transaction.price=getParamEx(class,security,"last").param_value+sign*10*getParamEx(class,security,"SEC_PRICE_STEP").param_value
        		end
        		transaction.price=toPrice(security,transaction.price,class)
        	else
        		transaction.price="0"
        	end
         
        	local res=sendTransaction(transaction)
        	if res~="" then
        		return nil, "sendMarket "..res
        	else
        		return trans_id, "Class="..class.." Sec="..security.." Dir="..direction.." Vol="..volume.." Acc="..account.." Trans_id="..trans_id..' Price='..transaction.price
        	end
        end

        Ставлю стоп так:

        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
        
         function sendTakeProfitAndStopLimit(class,security,direction,price,
        takeprice,stopprice,volume,SEC_PRICE_STEP,offsetunits,deffspreadunits,
        account,exp_date,client_code,comment)
         
           if class==nil or security==nil or direction==nil or price==nil
           or takeprice==nil or stopprice==nil or volume==nil or account==nil
           or offsetunits==nil or deffspreadunits==nil then
              return nil, "sendTakeProfitAndStopLimit Can`t send order. Nil parameters.";
           end
           local trans_id = random_max();
           local transaction = {
              ["TRANS_ID"]         = tostring(trans_id),
              ["ACTION"]           = "NEW_STOP_ORDER",
              ["CLASSCODE"]        = class,
              ["SECCODE"]          = security,
        	  ["ACCOUNT"]          = tostring(account),
              ["STOP_ORDER_KIND"]  = 'TAKE_PROFIT_AND_STOP_LIMIT_ORDER',
              ["OPERATION"]        = direction,
              ["QUANTITY"]         = string.format("%d", tostring(volume)),
              ["PRICE"]            = toPrice(security, price, class),  -- Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0)
              ["STOPPRICE"]        = toPrice(security, takeprice, class), -- Цена Тейк-Профита
              ["STOPPRICE2"]       = toPrice(security, stopprice, class),  -- Цена Стоп-Лосса
        	  -- "OFFSET" - (ОТСТУП)Если цена достигла Тейк-профита и идет дальше в прибыль,
              -- то Тейк-профит сработает только когда цена вернется минимум на 2 шага цены назад,
              -- это может потенциально увеличить прибыль
              ["OFFSET"]           = tostring(2*SEC_PRICE_STEP),
        	  ["OFFSET_UNITS"]     = offsetunits,-- Единицы измерения отступа ("PRICE_UNITS" - шаг цены, или "PERCENTS" - проценты)
        	  -- "SPREAD" - Когда сработает Тейк-профит, выставится заявка по цене хуже текущей на 100 шагов цены,
              -- которая АВТОМАТИЧЕСКИ УДОВЛЕТВОРИТСЯ ПО ТЕКУЩЕЙ ЛУЧШЕЙ ЦЕНЕ,
              -- но то, что цена значительно хуже, спасет от проскальзывания,
              -- иначе, сделка может просто не закрыться (заявка на закрытие будет выставлена, но цена к тому времени ее уже проскочит)
              ["SPREAD"]           = tostring(10*SEC_PRICE_STEP),
        	  ["SPREAD_UNITS"]     = deffspreadunits, -- Единицы измерения защитного спрэда ("PRICE_UNITS" - шаг цены, или "PERCENTS" - проценты)
        	  -- "MARKET_TAKE_PROFIT" = ("YES", или "NO") должна ли выставится заявка по рыночной цене при срабатывании Тейк-Профита.
              -- Для рынка FORTS рыночные заявки, как правило, запрещены,
              -- для лимитированной заявки на FORTS нужно указывать заведомо худшую цену, чтобы она сработала сразу же, как рыночная
              ["MARKET_TAKE_PROFIT"] = "NO",
        	  ["MARKET_STOP_LIMIT"]  = "YES",
        	  --["EXPIRY_DATE"]        = "TODAY", -- Срок действия стоп-заявки ("GTC" – до отмены,"TODAY" - до окончания текущей торговой сессии, Дата в формате "ГГММДД")
        	  --["COMMENT"]            = "Простой MA-робот ТЕЙК-ПРОФИТ и СТОП-ЛОСС"
           }
           if client_code == nil then
              transaction.client_code = tostring(account);
           else
              transaction.client_code = tostring(client_code);
           end
           if exp_date == nil then
              transaction["EXPIRY_DATE"] = "GTC";
           else
              transaction['EXPIRY_DATE'] = tostring(exp_date);
           end
           local res = sendTransaction(transaction);
           if res ~= "" then
              return nil, "sendTakeProfitAndStopLimit" .. res;
           else
              return trans_id, "Class=" ..class..
        	  " Sec=" ..security.. " Dir=" ..direction.. " Price=" ..price..  ' OffsetUnits=' ..offsetunits..
        	  ' SpreadUnits=' ..deffspreadunits.. " Vol=" ..volume.. " Acc=" ..account..
        	  " Trans_id=" ..trans_id;
           end
        end
          1. открываю продажу (функ sendMarket) на 1 лот ставлю стоп и тейк. потом открываю покупку (функ sendMarket) на 1 лот ставлю стоп и тейк. получается чистая позиция 0 и 2 стопа и 2 тейка (1 стоп и тейк на продажу и 1 стоп и тейк на покупку).

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

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

                    1. Такие заявки во время клиринга снимаются
                      «GTC» – до отмены,
                      «TODAY» - до окончания текущей торговой сессии,
                      Дата в формате «ГГММДД».

                      GTC, в данном случае, имеется в виду до отмены биржей, а не Вами. Назначайте дату с запасом небольшим, тогда не снимутся.

                    2. Могу предложить Вам готовую связку функций (может пригодится):

                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      
                      -- Возвращает значение поля EXRIRY_DATE для заявки
                      GetEXPIRY_DATE = function(days)
                         if days == nil then days = 7 end
                         local dt = GetDateTimeNow()
                         local seconds = os.time(dt) + 60*60*24*days
                         dt = os.date("*t",seconds)
                         return (dt.year - 2000)..dt.month..dt.day
                      end
                      -- Возвращает текущую дату/время сервера в виде таблицы datetime
                      GetDateTimeNow = function()
                         local dt = {}
                         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
                         return dt
                      end

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

                      1
                      
                      ["EXPIRY_DATE"] = GetEXPIRY_DATE()
                    3. Только имейте в виду, что на демо-счете от разработчиков квика нельзя выставлять заявки со временем действия

                    4. все равно снимаются
                      поставил today опять снялась.
                      может дело в инструменте, такие странности на SiH7

                    5. система-настройки-осн.настройки-торговля-закрытие-позиций-снимать стоп заявки
                      галочка нужна?

                    6. У меня стоит эта галочка, я даже не знал про нее 🙂 Но стоп-заявки остаются при закрытии позиции, сейчас специально проверил: купил, выставил тейк профит и стоп-лимит, продал, стоп-заявка осталась.
                      Если Вы на демо столкнулись с такой проблемой, то напишите сюда: quiksupport@arqatech.com
                      Если это на реальном счете, или на демо-счете брокера, то свяжитесь с брокером.

                      По идее, TODAY снимаются в вечерний клиринг, GTC только когда Вы снимите, ну и с датой понятно.
                      Прошу прощения, что раньше написал, что GTC биржей снимается, это не так. Если у Вас GTC снимается, то пишите тому, кто предоставил Вам счет, это ошибка.

                    7. а еще иногда бывает что заявка выставляемая по стоп заявке отвергается. Не знаете почему?

                    8. стем что отвергаются заявки я разобрался

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

                    10. робот не снимает, счет не демо. Инимается при попытке исполнения

  4. Я пока не очень разобрался в чтении из файлов - возможно ли, например, чтение строки из tri файла квика и сразу заполнение переменной Transaction. Или нужно парсить эту строку для каждого значения?

  5. В окне ввода заявки есть строка поручение, которое дает комментарий заявке. Из кода qlua я тоже хочу дать этот комментарий, решил что это COMMENT="мой комментарий" в структуре для sendTransaction, но нужного комментария в терминале не появилось?

      1. Огромное спасибо! Но я не понимаю как программировать, если ничего невозможно найти в хелпе, где брать информацию? Например, у вас в коде ["COMMENT"] = "Покупка опционов скриптом", сразу думаешь - вот как делать комментарии к боту. Но в qlua.chm вообще таково слова не находится ни одного, в info.chm находится "COMMENT - Текстовый комментарий, указанный в заявке. Используется при снятии группы заявок" вроде и то, но какие-то групповые заявки. Там же рядом "brokerref STRING Поручение ", вроде то что надо так как звучит даже как в окне ввода заявок строка "поручение" . Да и в qlua.chm тоже есть информация: "brokerref STRING Комментарий, обычно: / " - вроде прямым текстом что это и есть комментарий и действительно при вводе заявки вручную, если поле поручение не задавать , то в комментарий копируется код клиента... Путаница безумная. Вопрос нет ли какого-то более реального руковдства по qlua?

        1. Ой, я ошибся, простите, brokerref точно нужно использовать, тяжелый день был, голова немного не варит. С руководством действительно много неоднозначностей, все мучаются, для того и делал сайт, потому что сам намучился, когда изучал QLUA, решил немного облегчить задачу другим, не все, конечно, смог осветить, но, если что, спрашивайте, постараюсь помочь, чем смогу.

            1. Залез сейчас к себе в код и нашел ответ на загадку: при отправке транзакции комментарий указывается в поле CLIENT_CODE, а для того, чтобы в скрипте прочитать этот комментарий, читают поле brokerref ответа по транзакции, заявки, стоп-заявки, или сделки. По этому и получилась путаница такая, потому что оба ответа верны 🙂

            2. Пример из движка:

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              
              ...
                 -- Заполняет структуру для отправки транзакции
                 local Transaction={
                    ['TRANS_ID']   = tostring(SE_trans_id),-- Номер транзакции
                    ['ACCOUNT']    = account,              -- Код счета
                    ['CLASSCODE']  = class_code,           -- Код класса
                    ['SECCODE']    = sec_code,             -- Код инструмента
                    ['ACTION']     = 'NEW_ORDER',          -- Тип транзакции ('NEW_ORDER' - новая заявка)      
                    ['TYPE']       = 'L',                  -- Тип ('L' - лимитированная, 'M' - рыночная)
                    ['OPERATION']  = operation,            -- Операция ('B' - buy, или 'S' - sell)
                    ['PRICE']      = tostring(price),      -- Цена
                    ['QUANTITY']   = tostring(qty),        -- Количество
                    ['CLIENT_CODE']= 'SE_'..sec_code       -- Комментарий к транзакции, который будет виден в транзакциях, заявках и сделках в поле brokerref      
                 }
              ...
              function OnTrade(trade)
                 ...
                       if trade.sec_code == pos.sec_code and trade.brokerref:find('SE_'..pos.sec_code) then
              ...
                1. Дмитрий, какая-то странная штука. Тестирую скрипт на случай дисконнекта и пр. экстремальных ситуаций. Специально останавливаю скрипт и снимаю руками заявку на закрытие позиции, которую он контролирует. Заявка связана со стоп заявкой, но в данном случае, как я понимаю, это без разницы. Запускаю скрипт и подвожу к ситуации, когда, по логике, он должен эту заявку (которую я снял руками) снять и закрыться по рынку.
                  И он ее эту уже снятую заявку якобы снимает, по крайней мере Result = sendTransaction(Transaction) выдает ""! Если снимаю связанную стоп заявку - тот же результат.
                  Может это нормально и нужно проверять снята заявка или нет?
                  И вдогонку: а как вообще скрипт ведет себя пр разрыве, а потом возобновлении связи?

                    1. Дмитрий, я пишу про свой скрипт на LUA, который выставляет и снимает заявки. Я видимо просто не в том месте комментарий отправил ;(

                  1. Транзакция на снятие несуществующей заявки не вернет ошибку при отправке, то, что заявку не удалось снять, нужно смотреть в результатах транзакций, функция OnTransReply. На разрывы соединения скрипт сам по себе никак не реагирует, т.к. он обрабатывает данные по мере поступления, при разрывах они просто перестают поступать. Если Вам важно отслеживать разрывы, то можете использовать функции обратного вызова OnDisconnected и OnConnected, либо проверять есть ли соединение функцией isConnected.

                    1. Дмитрий, большое спасибо. Может быть можно так же смотреть заранее FLAGS в таблице заявок. trans_id я знаю?

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