alfacentavra
alfacentavra личный блог
31 июля 2023, 17:23

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

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

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

Сигналы на закрытие позиции.

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

Это всё тестируется и чаще берется какой-то один вариант в работу. Реже более сложная модель, совмещающая в разные сочетания несколько видов выходов.

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

Сигнал LONG:  в случае, если количество лотов на покупку (суммарный спрос) в 2 раза больше аналогичного объема на продажу (суммарное предложение), при этом текущая цена выше цены закрытия вчерашнего дня (основной торговой сессии).

Сигнал SHORT:  если суммарные продажи в 2 раза больше суммарного спроса и текущая цена ниже цены закрытия вчерашнего дня (основной сессии).

В нашем коде достаточно разместить в условиях отработки сигналов следующий код после else:

--отработка выхода из сигналов
if signal[i] ~= 0 then
  if signal[i] == 1 then
    message(progname.." закрытие позиции LONG по "..tikers[i].." по цене "..tLast.param_image)
  end
  if signal[i] == -1 then
    message(progname.." закрытие позиции SHORT по "..tikers[i].." по цене "..tLast.param_image)
  end
  signal[i] = 0
end


И если ранее соблюдались условия сигнала (signal[i] не равен 0), а теперь нет, то будет выдано необходимое сообщение и сигналу по инструменту присвоен ноль.

При этом, также как скользящие средние в боковике будут давать множество сигналов на открытие и закрытие позиции, так же и в нашем случае если нет сильного движения можно получить в коротком интервале множество входов и выходов по одному и тому же инструменту. Например только за первые 17 минут открытия торгов (скрипт начинает генерировать сигналы с 10:01) можно получить таким образом аж 22 сигнала на вход:

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

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

Отдельная таблица для вывода сигналов, логов и результатов работы скрипта.

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

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

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

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

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

Добавим сперва саму таблицу в main:

size_table = 10 -- количество строк таблицы
table_result_ind = 1 -- индекс строки
if table_result==nil then 
  table_result = AllocTable()
  AddColumn(table_result, 1, "Сигналы и результаты:", true, QTABLE_STRING_TYPE, 70)
  CreateWindow(table_result)
  SetWindowPos(table_result,0,430,500,300)
  SetWindowCaption(table_result, progname.." сообщения скрипта")
  for u = 1, size_table do
     InsertRow(table_result,-1)   
  end
end

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

 

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

function table_print(table_text, table_result_ind)

  SetCell(table_result, table_result_ind, 1, table_text)

  if table_result_ind == 1 then
    SetColor(table_result, 10, QTABLE_NO_INDEX, mWhite, mBlack, mWhite, mBlack)
    SetColor(table_result, table_result_ind, QTABLE_NO_INDEX, mBlue, mBlack, mBlue, mBlack)               
  else
    SetColor(table_result, table_result_ind - 1, QTABLE_NO_INDEX, mWhite, mBlack, mWhite, mBlack)
    SetColor(table_result, table_result_ind, QTABLE_NO_INDEX, mBlue, mBlack, mBlue, mBlack)
end

end

Во втором случае:

function table_print(table_text, table_result_ind)

if table_result_ind <=10 then
  SetCell(table_result, table_result_ind, 1, table_text)
else
  InsertRow(table_result,-1)
  SetCell(table_result, table_result_ind, 1, table_text)
end

end


Добавим в условиях сигнала соответствующий вывод в таблицу и обновление счетчика строки:

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

И мы получим необходимый результат.

Ссылка на github первого варианта.

Ссылка на github второго варианта.

Чтобы в нашей таблице отражалось еще и время сигнала достаточно, например, добавить в строку для отображения time3, которое мы отслеживаем в каждом случае if (помним, что отражение будет числом).

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


Вывод информации по финансовому результату позиции.

Удобно, чтобы скрипт мог считать финансовый результат. На боевых счетах это может быть точное вычисление с учетом всех особенностей тарифа брокера, плечей, FIFO или даже с учетом налогов. Но сейчас, в первичном исполнении советника, мы упростим и будем находить в качестве финансового результата простой валовый доход по трейду: разницу между покупкой или продажей для лонгов и продажей и дальнейшим откупом для шортов. Когда мы подойдем к работе с реальными счетами расчеты можно будет сделаем уже с учетом всех затрат. Пока простая арифметика для понимания насколько в целом успешна или убыточна взятая в качестве стратегии для советника гипотеза по инструментам.

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

Добавим 3 массива:

lots = {} -- лотность по инструменту
buypos = {} -- будем фиксировать цену покупки по каждой бумаги
sellpos = {} -- аналогично фиксируем цену продажи

Лоты можно запросить, например, в цикле, где получали цену закрытия предыдущего дня. Там же поставить нулевые значения массивам цен:

for x = 1, #tikers do
  closeprice[x] = getParamEx("TQBR", tikers[x], "PREVLEGALCLOSEPR") -- цена закрытия предыдущего дня
  signal[x] = 0 -- статус сигнала по инструменту
           
  lots[x] = getParamEx("TQBR", tikers[x], "LOTSIZE")
  buypos[x] = 0 -- добавляем нулевые значения в массив цен покупок
  sellpos[x] = 0 -- и в массив цен продаж
end

В условиях сигналов добавляем фиксацию цены входа (для шорта это цена продажи sellpos, для лонга это цена покупки buypos):

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

Нужно сказать, что данный пример входа в позицию – это очень большая условность, т.к. цена при срабатывании сигнала будет отличатся (в отдельные моменты значительно) от цены реального входа (более правильно – в случае советника брать ближайший BID/OFFER со стакана, работу с которым мы будем рассматривать в скором времени, а в случае роботов цену фиксируем по конкретной сделке, т.к. даже ориентируясь на стакан иногда приходится предусматривать повторное выставление заявки, если рынок в моменте резко сдвинулся в одну или другую сторону).

Теперь добавим в условиях сигналов if в части отработки выхода цену закрытия (обратная цене входа, т.е. для лонговых позиций это sellpos, для шортовых buypos), необходимые расчеты результата и вывод в нашу дополнительную таблицу:

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

Запускаем скрипт, получаем:

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

Округление цифр.

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

Кто впервые с этим столкнулся удивится, что привычные 0.1 для компьютера на самом деле 0.1000000000000000055511151231257827021181583404541015625, и данную погрешность порой
можно встретить даже в Excel просто чаще её нам Excel не показывает.

Например простой скрипт message(tostring(0.1+0.2 == 0.3)) вернет нам false, а не true, что было бы логичным. Подробнее об этом можно почитать в сети загуглив «погрешности при операциях с плавающей запятой», например.

К сожалению, просто отсечь эту погрешность до второго знака нельзя, как мы это делали ранее с нулями, т.к. иначе 0.9999999999998 превратится в 0,99, а не в 1. Нам нужна функция, которая сделает округления по принятым математическим правилам.

И проще всего это реализовать через строковое форматирование данного числа. Сделаем функцию:

function mprint(numb)
 return string.format("%.2f", numb)
end

Через которую будем выводить итоговый результат. Она округлит по всем правилам число до второго знака.

 

Кстати, если применить вывод с помощью форматирования к сумме 0,1+0,2 и к числу 0,3 с точностью до 30 знака

message(string.format("%.30f",0.1+0.2))
message(string.format("%.30f",0.3))

 

то увидим, почему эти числа для компьютера не равны:

0.300000000000000044408920985006

0.299999999999999988897769753748

Теперь в скрипте выдачу информации finres будем проводить через данную функцию mprint с точностью до 2 знака, в итоге получим уже нормальное отражение цифр:

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

Код варианта советника с учетом расчетов результата:

https://github.com/morefinances/qlua/blob/main/simple%20advisor%20v1_2_finres.lua

 

В качестве самостоятельных упражнений:

1. Сделайте расчет финансового результата с учетом комиссий брокера (например 0,05% от суммы за сделку).

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


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


Теги: 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: дополняем скрипт советника таймингом
Устанавливаем время старта работы скрипта,
Ставим тайминг на получение сигналов на вход,
Устанавливаем таймер на приостановку скрипта.

 


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

8 Комментариев
  • Сергей Иванов
    31 июля 2023, 18:25
    alfacentavra, спасибо
  • Сергей Иванов
    31 июля 2023, 18:33
    если в Excel хранятся ISIN и интересующие уровни покупки  можно ли как то в данный скрипт данные из Excel получить напрямую, а не через CSV файл обмена?
      • Сергей Иванов
        01 августа 2023, 16:40
        alfacentavra, добрый день
        я больше по облигациям — ISIN и уровни покупки/продажи хранятся в Excel файле
          • Сергей Иванов
            02 августа 2023, 14:35
            alfacentavra, спасибо за совет 
            1/ luacom у меня не взлетел, excel = luacom.GetObject(«Excel.Application») не видит работающий экземпляр Excel2019/Win10 
            2/ перехожу к плану «Файл обмена», подскажете нет ли рабочих примеров чтения/записи в файл. Пока нашел вот такой пример записи (https://quikluacsharp.ru/qlua-osnovy/iz-qlua-lua-v-excel-csv/) 
            Не подскажите пример чтения?

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн