Блог им. morefinances

Qlua: завершаем апгрейд советника.

Сегодня дополним наш алгоритм советника следующими пунктами:

1. Пропуск «поздних» сигналов на старте.
2. Обработка советником обрыва связи.
3. Сохранение сигналов и логов в файл.


Еще один пункт, связанный со временем, который был выбран для апгрейда советника – это пропуск сигналов на старте, если запуск скрипта состоялся не в начале торговой сессии (например любой старт после 10:30). Это может быть полезным, если выбрана активная внутридневная стратегия и сигналы полученные на старте скрипта, например в середине дня, могут быть уже не актуальными (с низким потенциалом прибыли) и лучше дождаться новых. Т.е. необходимо разделить сигналы на те, которые сгенерировались на старте и остальные сигналы, которые будем далее брать в работу. Сигнал на старте может закрыться (по обратному/сигналу выхода) и если переоткроется снова, то его уже можно брать в работу как новый.

В нашем скрипте сигналы по каждому инструменту (массив signal) ранее могли принимать значение:

0 – вне позиции по инструменту

1 или -1 лонг или шорт

2 или -2 пропуск лонга или шорта по timeout (после заданного времени, когда пропускаем сигналы).

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

Разместим в OnInit соответствующую переменную:

nomorningtime = 103000


В условиях отработки сигнала необходимо предусмотреть, что если проходим первую итерацию и время > nomorningtime, то уведомляем о пропуске и signal по инструменту присваиваем соответствующее значение. Если данное условие не выполняется, то работает старый блок if с проверкой на сигнал.

if startind == 0 and time3 > nomorningtime then
  SetCell(m_t, i, 7, "noSHORT (start pass)")
  table_text = "Пропуск на старте SHORT сигнала "..tikers[i].." по цене "..tLast.param_image
  signal[i] = -3    
else
  if time3 < nonewsignalstime then
    SetCell(m_t, i, 7, "SHORT")
    table_text = "сигнал SHORT по "..tikers[i].." по цене "..tLast.param_image
    signal[i] = -1
    sellpos[i] = tonumber(tLast.param_value)
  else
   SetCell(m_t, i, 7, "noSHORT (time out pass)")
   table_text ="пропускаем сигнал на SHORT по таймеру "..nonewsignalstime.." по "..tikers[i].." по цене "..tLast.param_image
   signal[i] = -2
  end    
end


Аналогично делаем для лонга:
 

if startind == 0 and time3 > nomorningtime then
  SetCell(m_t, i, 7, "noLONG (start pass)")
  table_text = "Пропуск на старте LONG сигнала по "..tikers[i].." по цене "..tLast.param_image
  signal[i] = 3
else
  if time3 < nonewsignalstime then
    SetCell(m_t, i, 7, "LONG")
    table_text = "сигнал LONG по "..tikers[i].." по цене "..tLast.param_image
    signal[i] = 1
    buypos[i] = tonumber(tLast.param_value)
  else    
    SetCell(m_t, i, 7, "noLONG (time out pass)")
    table_text = "пропускаем сигнал LONG по таймеру "..nonewsignalstime.." по "..tikers[i].." по цене "..tLast.param_image
    signal[i] = 2
  end
end

В отработке закрытия позиции предусматриваем, что signal может быть по модулю равен 3:

if math.abs(signal[i]) == 3 then

  if signal[i] > 0 then
    table_text = "Пропуск закрытия позиции LONG (nomorningtime) по "..tikers[i].." по цене "..tLast.param_image
  else
    table_text = "Пропуск закрытия позиции SHORT (nomorningtime) по "..tikers[i].." по цене "..tLast.param_image
  end

  textfinres = "Ближайший сигнал по "..tikers[i].." будет отработан на вход"

end

 

Запускаем после 10:30, получаем на старте следующую картину:

Qlua: завершаем апгрейд советника.

Qlua: завершаем апгрейд советника. 

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

Qlua: завершаем апгрейд советника.

Так, например, по Татнефть преф (TATNP) был сигнал при запуске скрипта, он через несколько минут был закрыт алгоритмом и позже уже предлагался на открытие позиции:

Qlua: завершаем апгрейд советника.

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

Небольшое дополнение: в прошлой статье пропустил в выводе финансового результата: т.к. сигналы 2 и -2 мы пропускаем – не входим их по таймеру, то и вывод финансового результата не понадобится. Ставим дополнительное условие по выводу результата:

Qlua: завершаем апгрейд советника.

Итоговый вариант файла с правками по пропуску «поздних» сигналов на старте:
https://github.com/morefinances/qlua/blob/main/simple%20advisor%20v1_3_nonewsignalstime.lua

 

Обработка обрыва связи.

Варианты того как можно обрабатывать разорванные соединения с сервером мы уже проходили, просто добавим переменную connect=isConnected() и в зависимости от того есть у нас связь или нет будем обновлять таблицу, либо выведем сообщение:

Qlua: завершаем апгрейд советника.

Переменная discreport (прописываем её в OnInit: discreport = 0) позволит нам вывести это сообщение только один раз (после вывода меняем значение с 0 на 1). А при возобновлении связи мы добавим перед условиями сигналов вывод:

Qlua: завершаем апгрейд советника.

При этом, если мы хотим, например, чтобы сигналы, которые получим при возобновлении связи не принимались в работу (по аналогии с рассмотренным запуском после nomorningtime), то нужно добавить в это условие всего 2 строки:

startind = 0
nomorningtime = time3 - 1

Старые позиции скрипт будет закрывать по тейку, а сигналы, которые нарисовались сразу после запуска пропускать до следующих новых по данным инструментам.

Qlua: завершаем апгрейд советника.

На старте имеем после разрыва связи 3 сигнала, которые скрипт пропускает. Спустя небольшое время по 1 происходит закрытие и скрипт далее готов по нему принимать новые сигналы:

Qlua: завершаем апгрейд советника.

 Qlua: завершаем апгрейд советника.

Через 15 минут скрипт уже получил закрывающие сигналы и отслеживает только новые позиции:

 Qlua: завершаем апгрейд советника.

Qlua: завершаем апгрейд советника. 

Итоговый файл с правками по обработки разрыва связи: https://github.com/morefinances/qlua/blob/main/simple%20advisor%20v1_3_disconnect.lua


Сохранение сигналов и логов в файл.


И на последок самое простое. Работу с файлами мы также уже проходили ранее
.

Наши сигналы и логи будем сохранять в файл в директории C:\files\ (для корректной работы нужно создать).

Наименование файла будет состоять из название самого советника плюс дата и время:

filename = string.sub(progname, 1, 14).." "..os.date('%d%m%Y_%H%M%S')

 

Есть 2 варианта реализации сохранения в файл.

Первый – самый простой, мы открываем файл на запись в main в начале исполнения скрипта:

--создаем/открываем файл для записи
DirectionSaveFile=tostring("C:\\files\\"..filename..".csv")
my_csv=io.open(DirectionSaveFile,"a+")

И закрываем при остановке скрипта в OnStop:

-- закрытие файла
my_csv:flush()
my_csv:close()

А во всех местах, где у нас есть вывод в файл мы размещаем:

my_csv:write(abc..";\n") 

где abc – данные для сохранения, ";\n" – знак разделителя и переноса строки.


Файл1:
https://github.com/morefinances/qlua/blob/main/simple%20advisor%20v1_3_save_file_easy.lua

Этот вариант вполне рабочий, но я бы назвал его «ленивым», т.к. делается быстро, но не совсем правильно: если по каким-то причинам у нас будет нештатно приостановлена работа скрипта (завис компьютер или вылетел квик, например), то данные не сохранятся в файл, т.к. всё это время работы скрипта данные только собирались в поток вывода, но не были сохранены (это делает flush, которую мы спрятали в OnStop).

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

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

Функция открытия файла:

function open_file()
 --создаем/открываем файл для записи
 DirectionSaveFile=tostring("C:\\files\\"..filename..".csv")
 my_csv=io.open(DirectionSaveFile,"a+")
end


Функция записи:

function print_file(text)
  my_csv:write(text..";\n")
end


И функцию закрытия файла:

function close_file()
  -- закрытие файла
  my_csv:flush()
  my_csv:close()
end


Файл2:
https://github.com/morefinances/qlua/blob/main/simple%20advisor%20v1_3_save_file_normal.lua

Запускаем скрипт, получаем за пару часов работы отработку сигналов:

Qlua: завершаем апгрейд советника.

И аналогичные записи в наш csv файл:

Qlua: завершаем апгрейд советника.

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


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

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

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

Упражнения:

1. Дополните скрипт закрытием оставшихся позиции и финальным расчетом результатов по ним при остановке скрипта.

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

3*. В логике сигналов при написании алгоритма я использовал небольшое упрощение (чтобы не перегружать код), однако если подходить более строго и писать боевого робота, то код нужно дополнить в условиях сигнала. Попробуйте найти ошибку в логике сигналов данного скрипта и корректно переписать условия для изначально выбранной стратегии.


Теги: qlua для начинающих, кружок авиамоделизма.


Ранее:

Qlua: введение
Доля клиентов, использующих алгоритмическую торговлю
«Кружок авиамоделизма»
Разные торговые терминалы, почему Quik
Основной функционал qlua

Настраиваем торговый терминал и редактор кода
Установка торгового терминала
Подготовка терминала
Вкладки в терминале
Сохранение и загрузка настроек
Таблица системных сообщений
Отключение окна сообщений
Редактор Notepad++
Настройка русского языка в редакторе
Выбор синтаксиса языка и кодировки
Цветовые настройки
Дублирующий просмотр
Запуск первого скрипта

Основы qlua, часть 1:
message, конкатенация
фильтрация по сообщениям в терминале
PrintDbgStr, комментарии
типы данных, type
операции с числами
операции со строками
операции с таблицами
условные операторы

Основы qlua, часть 2:
Циклы for … do … end, while do … end, repeat … until.
sleep, break, goto.
как пройти весь массив циклом, как пройти таблицу по ключам
локальные и глобальные переменные, функции
получение даты и времени
получение данных через getInfoParam

Qlua: структура скрипта.
Структура типового скрипта qlua с примерами.
Обработка скриптом «обрыва связи» с сервером и возобновления работы.
Работа с файлами: запись, перезапись и чтение файла.
getScriptPath, getWorkingFolder

Qlua: получение данных из таблицы текущих торгов, создание таблиц в торговом терминале.
Получение биржевых данных через функцию getParamEx
Выгрузка списка параметров функции getParamEx через DDE из торгового терминала
Создание пользовательских таблиц в торговом терминале

Qlua: работа с таблицами (продолжение). Пишем своего советника (начало).
Интегрируем таблицы в структуру скрипта qlua.
Удаляем таблицы через DestroyTable.
Останавливаем скрипт через IsWindowClosed.
Обработка события закрытия таблицы через коллбэк.
Работа с цветом SetColor, Highlight, SetSelectedRow.
Пишем простого советника.

Qlua: дополняем скрипт советника таймингом:
Устанавливаем время старта работы скрипта,
Ставим тайминг на получение сигналов на вход,
Устанавливаем таймер на приостановку скрипта.

Qlua советник: продолжение.
Дополняем сигналами на закрытие позиции.
Создаем дополнительную таблицу для вывода данных.
Делаем расчеты финансового результата.

  • обсудить на форуме:
  • Quik Lua
★13
5 комментариев
Два вопроса. Вот в таблице например указано — Продажа и Покупка по Алросе :567000 и 258000 — откуда черпается эта информация? И если алгоритм каждую минуту что то покупает и продает, как выглядит отчет брокера по таким сделкам?
avatar
Login12345, это не цены, это суммарные покупки и продажи, берутся из таблицы текущих торгов. Сама торговая логика рассматривалась в статьях ранее, где только начинали писать советника. Текущий алгоритм исключительно для учебных целей, об этом также ранее говорил.
avatar
Прикрутить бы еще что бы сигналы в телегу летели

avatar
Спасибо, за Ваш труд! Ваши примеры с доскональным разбором кода, очень хорошо подходят для самостоятельного обучения. Параллельно разбираю базу из lua.org.ru/contents_ru.html#3.3.4 и Руководство пользователя QLua. Информации в совокупности предостаточно, чтобы начать осваивать данный язык. Уверен что Ваш труд со временем разойдется по интернету, как один из самых полезных для начинающих по изучению QLua!!!
avatar
User24, спасибо! дальше будет самое интересное!)
avatar

теги блога alfacentavra

....все тэги



UPDONW
Новый дизайн