Skip to content

Latest commit

 

History

History
 
 

robot

Пример простого робота на qLua для терминала Квик.

Ссылка на сборку x64 в кодировке Win1251 https://yadi.sk/d/d45A0stWjcQ_SA

Update: адаптирован для lua 5.3 (Квик 8.5 и выше)

Данный робот предназначен для автоматизации торговли, обеспечения автоматического выставления стоп заявок и ведения их, вирутальной торговли. Вирутальная торговля нужна для обеспечения возможности совершать сделки для тестирования стратегий или просто тестовой ручной торговли. Данные робот содержит в себе модуль оптимизации и алгоритм работы со стоп заявками как и в тестере:https://github.com/nick-nh/qlua/tree/master/tester Это сделано для того, чтобы просто подключать оттестированные стратегии.

Free Image Hosting at FunkyIMG.com

Интерфейс робота содержит команды для ручной торговли, поля для смены интервала, стопа, тейк-профита и дополнительные команды.

Первая строка

Price - текущая цена инструмента Pos - размер и цена текущей позиции Profit - ведичина профита(в пунктах) текущей сделки SL - значение установленного стопа TP - значение установленного тейка Type - текущее напрвление торговли алгоритма INTERVAL - интервал для торговли Algo - текущее расчитанное значение алгоритма, направление торговли State - состояние робота

Вторая строка

START - запуск алгоритма робота qty - количество для открытия позиции, признак вида торговли (виртуальная, реальная) SELL - продать вручную BUY - купить вручную REVERSE - переворот CLOSE ALL - закрыть позицию KILL ALL SL - убрать все стоп заявки SET SL/TP - установить стоп заявки

Третья строка

В этой строке выводятся пресеты для торговли. Пресет - это набор предопределенных настроек, которые можно быстро применить. Пресет представляет собой таблицу, определяющую поведение робота. Для примера:

{
    Name    = "simpleM3",  -- имя пресета                 
    NAME_OF_STRATEGY = 'simple', -- имя стратегии
    ACCOUNT           = 'SPBFUT000jo',        -- Идентификатор счета для этой настройки
    CLIENT_CODE = "SPBFUT000jo", -- "Код клиента" для этой настройки
    SEC_CODE = 'MMH9', -- код инструмента для торговли
    CLASS_CODE = 'SPBFUT', -- класс инструмента
    QTY_LOTS = 1, -- количество для торговли
    OFFSET = 2, --(ОТСТУП)Если цена достигла Тейк-профита и идет дальше в прибыль
    SPREAD = 10, --Когда сработает Тейк-профит, выставится заявка по цене хуже текущей на пунктов,
    ChartId = "Sheet11", -- индентификатор графика, куда выводить метки сделок и данные алгоритма. 
    SetStop = true, -- выставлять ли стоп заявки
    fixedstop = false,-- STOPLOSS не рассчитывать по алгоритму, а брать фиксированным из настроек
    isLong  = true, -- доступен лонг
    isShort = true, -- доступен шорт
    trackManualDeals = true, --учитывать ручные сделки не из интерфейса робота,
    maxStop       = 85, -- максимально допустимый стоп в пунктах                  
    reopenDealMaxStop       = 75, -- если сделка переоткрыта после стопа, то максимальный стоп                  
    stopShiftIndexWait       = 17, -- если цена не двигается (на величину стопа), то пересчитать стоп после стольких баров                   
    shiftStop = true, -- сдвигать стоп (трейил) на величину STOP_LOSS                 
    shiftProfit = true, -- сдвигать профит (трейил) на величину STOP_LOSS/2
    reopenPosAfterStop       = 7, -- если выбило по стопу заявке, то попытаться переоткрыть сделку, после стольких баров                  
    INTERVAL          = INTERVAL_M3, -- Таймфрейм графика
    testSizeBars = 540, -- размер окна оптимизации стратегии
    calculateAlgo = simpleAlgo, -- имя функции расчета алгоритма
    iterateAlgo = iterateSimpleAlgo, -- имя функции подготовки таблицы набора параметров для оптимизации
    initAlgo = initSimpleAlgo, -- имя функции для обнуления таблиц алгоритма перед очередным шагом оптимизации
    setTableAlgoParams  = setTableSimpleAlgoParams, -- имя функции вывода параметров в интерфейс
    readTableAlgoParams = readTableSimpleAlgoParams, -- имя функции считывания параметров из интерфейса 
    readOptimizedParams = readOptimizedSimpleAlgo, -- имя функции чтения оптимальных параметров алгоритма из файла
    saveOptimizedParams = saveOptimizedSimpleAlgo, -- имя функции записи оптимальных параметров алгоритма в файл
    settingsAlgo = 
    {
        shift = 16, -- перменная алгоритма
        STOP_LOSS         = 25,                   -- Размер СТОП-ЛОССА
        TAKE_PROFIT       = 130                   -- Размер ТЕЙК-ПРОФИТА
    }
}

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

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

Подключаются модули просто:

dofile(getScriptPath().."\\regAlgo.lua") --Reg алгоритм

dofile(getScriptPath().."\\thvAlgo.lua") --THV алгоритм

dofile(getScriptPath().."\\nrtrAlgo.lua") --NRTR алгоритм

dofile(getScriptPath().."\\shiftMaAlgo.lua") --сдвиг EMA алгоритм

Пресет по-умолчанию определяется переменной curPreset = 1

Если использовать робот только как инструмент для ручной торговли, то надо определить один пресет. Для простоты, я включил образец, включающий тривиальный алгоритм торговли. Меняя функцию simpleAlgo можно менять данный алгоритм.

Последние две колонки третьей строки - это команда OPTIMIZE - запуск оптимизации алгоритма, информационная строка о накопленной прибыли.

Далее идут строки параметров алгоритмов (пресетов). Их разное количество для каждого алгоритма. Поэтому, если требуется, то добавляются или удаляются строки при выборе пресета.

Теперь про методику работы.

Для начала определите: default_ACCOUNT = '77777777' -- Идентификатор счета default_CLIENT_CODE = "777777777" -- "Код клиента"

Также, для каждого пресета, можно задать свой счет и класс. Т.о. можно торговать и акциями и фьючерсами на разных счетах.

После запуска скрипта, робот находится в стостоянии Остановлен. Запуск алгоритма осуществляется командой START.

Определены пременные, определяющие временное окно совершения сделок. startTradeTime = 1018

endTradeTime = 1842

eveningSession = 1900

Т.о. даже при работающем алгоритме сделки совершаются только в этом окне. Если запустить робота в 10:00, то он будет ждать наступления 10:18 и откроет сделку по рассчиатанному направлению. При наступлении endTradeTime открытые позиции принудительно закрываются. Чтобы этого избежать достаточно остановить алгоритм командой STOP.

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

Дополнительно написана функция checkSLbeforeClearing снимающая стоп заявки перед дневным клирингом. Если есть желание не снимать стоп, то надо просто закомментировать ее. Или же в этой процедуре можно прописать свои правила снятия и восстановления стоп заявок.

Правила открытия позиции определены в функции getTradeState. В ней происходит проверка условия на открытие позиции, вывод изменений в интерфейс и вызов процедуры расчета алгоритма для нового бара.

Правила торговли определяются таблицей trend, расчитываемой алгоритмом. Вход в сделку происходит, если происходит смена тренда с -1 на 1 и обратно. А закрытие сделки при смене тренда на 0. Поэтому алгоритм должен обеспечить расчет трех значений: calcAlgoValue, trend, calcChartResults, где calcAlgoValue - это значение алгоритма, от которого будет вычисляться стоп calcChartResults - это данные для вывода на график линий, помогающих визуально оценивать сделки. Для примера, алгоритм смещенных EMA возвращает calcChartResults[index] = {TEMA[index], EMA[index-shift-1]}. Т.е. это таблица из двух таблиц. А алгоритм NRTR просто возвращает таблицу NRTR, т.к. линия одна.

Встроенный тестовый алгоритм:

calcAlgoValue[index] = dValue(index, 'T') -- typical price
dVal[index]= dValue(index, 'C') -- цена закрытия

local isUpPinBar = DS:C(index)>DS:O(index) and (DS:H(index)-DS:C(index))/(DS:H(index) - DS:L(index))>=0.5 -- это пина-бар
local isLowPinBar = DS:C(index)<DS:O(index) and (DS:C(index)-DS:L(index))/(DS:H(index) - DS:L(index))>=0.5 -- это пин-бар

--покупка если не пин-бар и T цена на этом баре превысила цену бара на shift назад и T цена больше цены закрытия бара на shift назад
local isBuy = (not isUpPinBar and calcAlgoValue[index] > dVal[index-shift] and calcAlgoValue[index-1] <= dVal[index-shift-1]) and dVal[index] > dVal[index-shift]
--продажа если не пин-бар и T цена на этом баре пробила вниз цену бара на shift назад и T цена меньше цены закрытия бара на shift назад
local isSell = (not isLowPinBar and calcAlgoValue[index] < dVal[index-shift] and calcAlgoValue[index-1] >= dVal[index-shift-1]) and dVal[index] < dVal[index-shift]

-- определяем значение тренда на этом баре
if isBuy then
    trend[index] = 1 
end
if isSell then
    trend[index] = -1
end

--передаем для вывода на график две линии
calcChartResults[index] = {calcAlgoValue[index], dVal[index-shift]}

Сделки осуществляются рыночными заявками, путем определния цены заведомо больше/меньше ближайшей цены в стакане. Это важно. Для изменения данного поведения надо изменить алгоритм в функции trade. Такой алгоритм сделан, т.к. мои основные алгоритмы - это заявки по рынку.

Подробнее о стопе и тейке (аналогично как и в тестере https://github.com/nick-nh/qlua/tree/master/tester): Стопы, как и сделки определены рыночными. Не все брокеры дают возможность выполнять рыночные стопы на срочном рынке, поэтому цена высталения стоп-заявки после активации также рассчитывается заведомо больше/меньше ближайшей цены в стакане. Это определяется в функции SL_TP math.floor(getParamEx(CLASS_CODE, SEC_CODE, 'PRICEMAX').param_value - 200*SEC_PRICE_STEP), т.е. цена высталения стопа будет у ближайшей границы допустимой цены дня.

Встроенный алгоритм реализует трейлинг стопа и тейка. Размер стопа и тейка указывается в пунктах цены. При входе в сделку открывается стоп по формуле: calcAlgoValue[index-1] - (kATRATR[index-1] + SL_ADD_STEPSSEC_PRICE_STEP, где calcAlgoValue[index-1] - это расчетное значение алгоритма на прошлом баре, для примера - рассчитанное EMA. kATR = 0.95 (переменная, можно изменить) ATR[index-1] - рассчитанное значение АТР на прошлом баре. SEC_PRICE_STEP - минимальный шаг цены инструмента SL_ADD_STEPS - отступ в шагах цены Т.о. стоп рассчитывается и не зависит от стопа, указанного в файле параметров. Если указать в настройках пресета fixedstop = true, то стоп будет фиксированным, указанным в настройках, независящим от алгоритма и волатильности.

Далее, введена переменная maxStop. Если рассчиатанный стоп превысит максимальный, то он ограничивается.

При ручной торговле стоп устанавливется в размере указанном в интерфейса по формуле: AtPrice - STOP_LOSS/leverage, где AtPrice - это цена открытой позиции. Т.о. ручная торговля - это четкий стоп, алгоритмическая торговля, стоп, расчитанный по алгоритму, если не указан фиксированный стоп.

Тейк профит рассчитывается просто lastDealPrice + TAKE_PROFIT/leverage

Т.е. он фиксируется в пунктах, указанных в настройках

Далее, по мере движения цены происходит сдвиг стопа и тейка. Правила сдвига прописаны в функции trailStop. Сдвиг происходит в двух случаях. Первый, если цена прошла размер указанного стопа в пунктах. Для примера, вход в сделку по цене 69450, стоп указан в 40 пунктов. Если прошла сделка по цене 69490, то происходит сдвиг. Второй, если с момента входа в сделку прошло указанное количество баров в переменной stopShiftIndexWait (по умолчанию = 17). Зачем нужен второй случай. Т.к. стоп плавающий, завистит от АТР, то изначально он мог быть большим. Далее снижается волатильность, а цена не двигается, т.е. сдвига по первому типу не произойдет. В этом случае полезно пересчитать стоп т.к. может возникнуть ситуация резкого движения, потрери от которго лучше ограничить с учетом нового значения волатильности. Т.к. стоп в таком алгоритме зависит от АТР, то алгоритм расчета должен обеспечить расчет АТР на кадом баре.

Первый сдвиг стопа происходит в безубыток. Далее, уже по мере движения цены от calcAlgoValue[index-1], т.к. значение алгоритма следует за ценой. Тейк сдвигается по формуле tpPrice + STOP_LOSS/leverage/2, где tpPrice - значение прошлого тейка.

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

Если произошло закрытие сделки по тейку или стопу, то реализована возможность переоткрытия сделки через reopenPosAfterStop (по умолчанию = 7). Т.о., если выбило по стопу, то через каждые reopenPosAfterStop будет предпринята попытка открыть сделку при условии, что текущая цена выше или ниже цены закрытой сделки. Это важно. Т.о., если выбило по стопу, а цена пошла в нужном направлении, то переоткроется сделка, если цена так и не развернулась, то сделка не откроется. Тоже самое с тейком. Бывают ситуации когда вышли по тейку, а цена идет дальше, в этом случае через reopenPosAfterStop будет открыта сделка заново, в надежде на продолжение движения.

Для более гибкого управления позицией можно в настройках указать: shiftStop = true, -- сдвигать стоп (трейил) на величину STOP_LOSS
shiftProfit = true, -- сдвигать профит (трейил) на величину STOP_LOSS/2 Т.о. можно не двигать стоп, не двигать тейк или оба. Т.е. стоп и профит будут фиксированы при входе в позицию. Если указать STOP_LOSS=0 или TAKE_PROFIT=0, то не будет устанавливаться стоп или тейк. Т.е. можно сделать установку только стопа и трейлить его.

Робот имеет встроенный оптимизатор параметров алгоритма. Его принцип работы аналогичен тестеру https://github.com/nick-nh/qlua/tree/master/tester. Запуск оптимизации выполняется командой OPTIMIZE. Также написана функция reoptimize, которая запускается после закрытия сделок перед вечерним клирингом. Также в функции CloseAll написан код, запускающий реоптимизацию алгоритма, если робот совершил две неудачные сделки подряд. Это часть кода закомментирована. Ее можно использовать как пример или напрямую.

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

После завершения оптимизации, происходит запись лучших параметров в файл по шаблону PARAMS_FILE_NAME = getScriptPath().."\robot"..NAME_OF_STRATEGY.."_"..SEC_CODE.."_int"..tostring(INTERVAL).."_params.csv" -- ИМЯ ЛОГ-ФАЙЛА (robotsimple_MMZ8_int3_params.csv)

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

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

Вирутальная торговля вполняется с имитацией реальной торговли. Реальные сделки не совершаются. Данный режим был реализован для имитации демо счета на реальных данных. Включается даный режим через переменную virtualTrade = true

Для быстрого переключения между вирутальной и реальной торговлей предусмотрена горячая комбинация клавиш: Shift+V. При этом происходит перезапуск настроек алгоритма с считываением текущей позиции в реальном режиме.

Количество лотов на одну сделку определяется переменной QTY_LOTS = 1 -- Кол-во торгуемых лотов Значение может быть изменено в интерфейсе, в колонке qty.

В режиме торговли алгоритма допускается добалять позиции вручную. Робот автоматически пересчитает "среднюю". В случае получения сигнала на разворот или закрытия позиции будет закрыт весь объем с учетом ручного добора. Автоматически открывается позиция размера QTY_LOTS.

Также определены перменные, определяющие надо ли открывать лонг, шорт, ставить ли стопы. SetStop = true isLong = true isShort = true

Данный робот передается как есть. Запуская его на реальном счете в торговом режиме вы это делаете на свой страх и риск.

Установка простая - все положить в любую папку и добавить в скрипты Квика robotAlgo. Для вывода на график линий алгоритмов надо использовать библиотеку StaticVar и индикатор algoResults из тестера https://github.com/nick-nh/qlua/tree/master/tester Чтобы при вирутальной торговле выводились метки сделок, надо определить ChartId в пресете, а также положить в папку установки робота папку с изображениями из тестера.

Update:

Интерфейс робота формируется декларативно исходя настроек в файле robotTable.lua. Использована методика Qtable Wrapper

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

Пример настроек алгоритма:

--Куда поместить кнопку выбора настройки

presets[newIndex].interface_line = 3 -- строка в которой будет расположена команда

presets[newIndex].interface_col = 2 -- колонка в которой будет расположена команда

--Какие значения настроек надо вывести в интерфейс, в указанные места

--Описание полей интерфейса

-- caption_line = строка где будет расположен заголовок поля

-- caption_col = колонка где будет расположен заголовок поля

-- val_line = строка где будет расположено значение поля

-- val_col = колонка где будет расположено значение поля

presets[newIndex].fields = {}

presets[newIndex].fields['period'] = {caption = 'period' , caption_line = 4, caption_col = 1 , val_line = 5, val_col = 1, base_color = nil} presets[newIndex].fields['shift'] = {caption = 'shift' , caption_line = 4, caption_col = 2 , val_line = 5, val_col = 2, base_color = nil} presets[newIndex].fields['koef'] = {caption = 'koef' , caption_line = 4, caption_col = 3 , val_line = 5, val_col = 3, base_color = nil} presets[newIndex].fields['periodATR'] = {caption = 'periodATR' , caption_line = 4, caption_col = 4 , val_line = 5, val_col = 4, base_color = nil} presets[newIndex].fields['kATR'] = {caption = 'kATR' , caption_line = 6, caption_col = 1 , val_line = 7, val_col = 1, base_color = nil}

-- возможность редактирования полей настройки

presets[newIndex].edit_fields = {}

presets[newIndex].edit_fields['period'] = true

presets[newIndex].edit_fields['koef'] = true

presets[newIndex].edit_fields['shift'] = true

presets[newIndex].edit_fields['periodATR'] = true

presets[newIndex].edit_fields['kATR'] = true

Т.о. задавая настройки в модуле, скрипт автоматически выведет команды и поля в интерфейс. Рисовать и программировать его не требудется.