Блог им. morefinances

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

 

  • обсудить на форуме:
  • Quik Lua
★8
8 комментариев
alfacentavra, спасибо
если в Excel хранятся ISIN и интересующие уровни покупки  можно ли как то в данный скрипт данные из Excel получить напрямую, а не через CSV файл обмена?
Сергей Иванов, раньше это делалось, например, с помощью luacom (https://quik2dde.ru/viewtopic.php?id=213), но не уверен, что есть сборка под последние версии терминала. а для какой задачи именно такая связка c Excel, принципиально без csv?
avatar
alfacentavra, добрый день
я больше по облигациям — ISIN и уровни покупки/продажи хранятся в Excel файле
avatar
Сергей Иванов, ясно. Просто любой сохраненный файл в Excel это фактически тот же csv.
Даже если не хочется работать с форматом csv, а нужен именно xls файл с формулами и форматированием, то можно сделать небольшой макрос, который нужные данные сохранит по кнопке (или при активации нужной ячейки, например) в csv, а скрипт на qlua уже все данные возьмет с csv. Зато работать это всё будет а) быстрее и б) более устойчиво, т.к.  делается полностью штатными средствами.
avatar
alfacentavra, спасибо за совет 
1/ luacom у меня не взлетел, excel = luacom.GetObject(«Excel.Application») не видит работающий экземпляр Excel2019/Win10 
2/ перехожу к плану «Файл обмена», подскажете нет ли рабочих примеров чтения/записи в файл. Пока нашел вот такой пример записи (https://quikluacsharp.ru/qlua-osnovy/iz-qlua-lua-v-excel-csv/) 
Не подскажите пример чтения?
avatar
Сергей Иванов, да, это хороший ресурс по qlua.
Работу с файлами, в т.ч. чтение рассматривал здесь: 
smart-lab.ru/blog/922044.php

Про запись в файл рассматривал еще в сегодняшней статье:
https://smart-lab.ru/blog/927748.php 
avatar
Сергей Иванов, вот по этой ссылке можно взять работающую dll:
wdho.ru/f4704dc, разместите в директории терминала.
Вышеприведенный пример по первой ссылке работает (выдает данные с ячеек A1 и B1). Только его нужно поместить в main.
avatar

теги блога alfacentavra

....все тэги



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