Найти тему
Код торгового робота

Мысли о неприменении Тейк-Профита. Подтягиваем стоп скриптом на QLUA.

Всем привет!

Относительно недавно у меня был в каком-то виде спор с моим другом на счет применения Тейк-профита. Вообще, достаточно классический подход: зашел в сделку, поставил стоп, поставил цель или Тейк-Профит. Если цена упала до стопа, то сделка закрылась в минус, если цена дошла до тейк-профита, то сделка закрылась в плюс. Вроде все понятно. Но, моя позиция состоит в том, что тейк-профит ставить не надо. Это вредно. Вместо тейк-профита нужно подтягивать стоп, если цена пошла в вашем направлении. С моим другом мы рассматривали разные ситуации: и когда игнорирование тейка приводило к небольшим, но потерям, нежели если бы закрылись по Тейку. Были ситуации и когда закрытие по тейку было бы преждевременным – цена продолжила движение в нужном направлении.

Повторюсь: лично мой подход – не использовать тейк-профит, а подтягивать стопы. Приведу в свою пользу цитату из книги Ларри Вильямса «Долгосрочные секреты краткосрочной торговли»:

«Легендарный Джесси Ливермор выразился лучше всего – Отнюдь не мои размышления, а только моя усидчивость принесла мне большие деньги. Да, моя усидчивость! … Я пытаюсь объяснить вам, что только большие колебания (в пределах временного интервала, выбранного вами для вашей торговли), которые мне удавалось поймать, позволили мне сделать миллионы долларов на торговле. Я, наконец, понял, что должен позволить моим прибылям накапливаться, чтобы компенсировать убытки, столь же естественные для этой игры, как дыхание для жизни. Потери в трейдинге абсолютно неизбежны. Такова аксиома, и это обязательно случится, что вызывает естественный вопрос: что мы можем сделать, чтобы компенсировать эти перехваченные у нас куски? Есть только два способа сгладить эту неприятность: мы должны или иметь очень низкий процент убыточных сделок, и/или существенно более высокую по сравнению с убытком среднюю прибыль. Время, и только время, даст вам большую прибыль: не размышления, не игра воображения, не попытки покупать и продавать на каждой вершине и каждом дне. Это игра для дураков. … самое главное, вы должны были научиться держаться за прибыльные сделки до конца временного интервала, в котором торгуете. Что касается меня, то я торгую по 2 – 5- дневным колебаниям. Всякий раз, когда моя жадность заставляла меня хватать быструю прибыль или пересиживать временной период, в котором я торгую, мне приходилось дорого платить за это. …. Заполучив прибыльную позицию, держитесь за нее, не растрачивайте впустую удачу. Разве возможно поймать крупное движение, если выходить из рынка задолго до его окончания?»

Ещё я хотел бы порекомендовать посмотреть видеоролик, в котором Казахстанский миллиардер Маргулан Сейсембаев рассказывает о том, как он продавал компанию мобильной связи K-Mobile. (https://yandex.ru/video/preview/13166831628276632379). Конечно, тут прямой связи с торговлей на бирже и выставлением тейк-профитов нет. Здесь прослеживается подход успешных людей к фиксации прибыли, к предоставлению жизни дать шанс вырасти прибыли. Считаю, что именно такой подход должен быть и в торговле на бирже – не нужно хвататься за сформировавшуюся прибыль – дайте шанс этой прибыли вырасти.

Хотя, безусловно, у подхода применения тейк-профита есть плюс: не нужно постоянно двигать стоп-заявку. Тем более, если торговля ведется по нескольким инструментам в течении дня. И это не маловажно – нельзя быть помешанным на торговле. Но, как раз применение скриптов на QLUA может помочь в перемещении стопов.

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

Алгоритм должен работать так:

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

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

Давайте для начала накидает блок-схему нашего алгоритма.

Блок-схема алгоритма по корректировке стоп-приказа
Блок-схема алгоритма по корректировке стоп-приказа

Реализованный код посмотреть можно тут.

Скачать тут.

Большинство действий реализовано в виде отдельных, достаточно простых, функций.

Отдельно нужно показать блок, который инициализирует исходные данные, это функция init()

function init()
CLASS_CODE = "QJSIM" -- Код класса
SEC_CODE = "SBER" -- Код бумаги
razmer_lota = give_info_razmer_lota(CLASS_CODE,SEC_CODE) -- получаем информацию о размере лота (сколько акций в одном лоте)
price_step = give_info_shag_price(CLASS_CODE,SEC_CODE) -- получаем информацию о шаге цены.
time_delay_standart = 10000; -- стандартная задержка времени между итерациями.
time_delay0 = 100; -- задержка времени между итерациями при снятой стоп-заявке.
time_delay_current = time_delay_standart; -- по умолчанию задержка стандартная.
Firm_ID="NC0011100000"; -- здесь нужно ставить свои данные
Kod_klienta="10776";-- здесь нужно ставить свои данные
Torg_schet="NL0011100043";-- здесь нужно ставить свои данные
Subscribe_Level_II_Quotes(CLASS_CODE, SEC_CODE) --делаем заказ получения данных по стакану.
sleep(500); -- подождем, на всякий случай.
difference_price_stop = 0.1; -- устанавливаем разницу между текущей ценой и устанавливаемым стопом. В процентах. К примеру, указание 0.1 - означает установление стоп-заявки на уровне ниже текущей цены на 0,1%.
trans_id = 1;
end

Здесь указывается бумага, то которой будет идти работа. Я работал на демо-счете, и тут у Сбера класс «QJSIM», на реальном счете класс у СБЕРа – «TQBR». С помощью отдельных функций определяется размер лота, шаг цены. Отдельно хотел бы сказать про указанные интервалы задержки времени. Дело в том, что в алгоритме при выявлении ситуации несоответствия цены стоп-заявки данная стоп-заявка снимается и сразу же не выставляется. Подразумевается, что она будет выставлена на следующей итерации. Но, тут возникает ситуация, что в этот момент все будет висеть без стоп-заявки. Поэтому в функции init() обозначено стандартное время между итерациями и время между итерациями в ситуации, когда снята стоп-заявка, а новая не выставлена. В переменной difference_price_stop устанавливается процент на ниже которого относительно текущей цены будет устанавливаться стоп-заявка.

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

function main()
init() -- инициируем установку исходных данных.
for time_kontrol = 1, 60000 do --Цикл для постоянной работы робота
count_stock = give_balance_stock(Firm_ID, Kod_klienta, SEC_CODE, Torg_schet); -- получить информацию о количестве бумаг в портфеле. Информация в количестве акций (Не в количестве лотов!)
if (count_stock == 0) then goto enditeration; -- если бумаг нет, то завершаем итерацию. ждем.
end
price_stop_order = give_price_stop_order(SEC_CODE); -- получаем цену установленной стоп-заявки. Если получим 0 - то стоп-заявка не установлена.
current_price = give_current_price(CLASS_CODE, SEC_CODE); -- получаем текущую цену по бумаге из стакана.
message("Текущая цена = "..current_price);
if (price_stop_order == 0) then -- если СТОП-зяавка не установлена, то устанавливаем ее.
kolvo_lotov_for_stop = math.ceil(count_stock / razmer_lota); -- определеяем количество лотов для выставления стоп-заявки.
price_for_stop = math.ceil((current_price * (1 - (difference_price_stop / 100))) / price_step) * price_step -- формируем цену стоп-приказа, что бы она была кратной шагу цены. И была ниже заданного значения относительно текущей цены.
message("Нужно установить стоп-заявку по цене: "..price_for_stop);
make_stop_order(CLASS_CODE, SEC_CODE, kolvo_lotov_for_stop, price_for_stop); -- устанавливаем стоп-заявку.
end
if (price_stop_order > 0) then -- если стоп-заявка установлена, то нужно проверить ее цену. И если она имеет большой отрыв от текущей цены, то снять ее.
if (price_stop_order / current_price) < (1 - (difference_price_stop/100)) then
number_stop_order = give_number_stop_order(SEC_CODE); -- получаем номер стоп-заявки.
delete_stop_order(number_stop_order) -- удаляем стоп-заявку по указанному номеру.
message ("Нужно удалить стоп-заявку.");
time_delay_current = time_delay0; -- временно уменьшаем время задержки между итерациями.
end
end
::enditeration::
sleep(time_delay_current);
time_delay_current = time_delay_standart; -- устанавливаем стандартную задержку времени между итерациями.
end
end

Тут хотел бы сделать несколько комментариев:

Если цена стоп-заявки низкая, то скрипт просто ее удаляет, сразу не выставляя новую. Новая выставится на следующей итерации.

При выставлении стоп-заявки, мы должны указывать цену кратную шагу цены. Если, например, по СБЕРу шаг цены 0,01 руб, то мы не можем выставить цену 287,6541 руб. Хотя расчетные формулы могут нам выдать такую величину.

Чтобы не было таких проблем, в этой строке цена выставляемого стоп-приказа нормализуется и делается кратной шагу цены:

price_for_stop = math.ceil((current_price * (1 - (difference_price_stop / 100))) / price_step) * price_step -- формируем цену стоп-приказа, что бы она была кратной шагу цены. И была ниже заданного значения относительно текущей цены.

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

Как вариант можно было брать просто текущую цену. Это была бы цена по которой совершилась последняя сделка. В этом случае можно было бы прочитать свечи и взять цену закрытия последней свечи. Как читать свечи на QLUA было рассмотрено в этом выпуске.

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

function give_balance_stock(Firm_ID, Kod_klienta, SEC_CODE, Torg_schet) -- функция возвращает количество бумаг в портфеле по коду бумаги.
local result;
local bumagi = getDepoEx(Firm_ID, Kod_klienta, SEC_CODE, Torg_schet,0)
if (bumagi) then
result = bumagi["currentbal"];
else
result = 0;
end
return result;
end

Ну, что же. Теперь давайте посмотрим, как на демо-счете работает данный скрипт.

Для большей наглядности я записал видео, в котором продемонстрировал работу данного скрипта. Видео можно посмотреть по ссылке: https://dzen.ru/video/watch/64e1c6641d913f68bba51db1

Давайте откроем окно запуска скриптов, таблицу сделок, таблицу заявок, таблицу стоп-заявок, таблицу сообщений, котировочный стакан по СБЕРу и запустим скрипт.

-3

Поначалу ни каких действий не происходит, так как у нас нет купленных акций Сбера. Соответственно стоп не устанавливается. Давайте купим один лот акций.

-4
-5

И практически сразу мы получаем сообщение о выставлении стоп-заявки по цене 231,57. При текущей цене 231,8.

И пока я тут писал эти строки. Стоп несколько раз сдвинулся. Вот таблица стоп-заявок.

-6

Как видите стоп-цена передвигается вверх. Вслед за ростом цены.

Вот что выдается в таблице сообщений:

82 22.05.2023 21:52:04 Текущая цена = 232.23
83 22.05.2023 21:52:04 Нужно удалить стоп-заявку.
84 22.05.2023 21:52:04 Текущая цена = 232.23
85 22.05.2023 21:52:04 Нужно удалить стоп-заявку.
86 22.05.2023 21:52:04 Стоп-заявка с номером N [10956307] снята.
87 22.05.2023 21:52:04 Не удается снять стоп-заявку N [10956307]
88 22.05.2023 21:52:04 Текущая цена = 232.23
89 22.05.2023 21:52:04 Нужно установить стоп-заявку по цене: 232.0
90 22.05.2023 21:52:04 Стоп-заявка N [10956308] зарегистрирована.
91 22.05.2023 21:52:14 Текущая цена = 232.17

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

  • Этот код написан в учебных целях, и не предполагается его использование в качестве самостоятельного инструмента. Он должен быть внедрен в какой-то более масштабный скрипт или запускаться, когда цена преодолела определённый уровень;
  • Скрипт не работает со фьючерсами. Для фьючерсов применяется другая команда определения остатков и параметров. Как определить остатки по фьючерсам было описано в этой статье.
  • Скрипт работает только для ситуации покупки. Для работы с короткими позициями нужно скрипт переделывать.

Вот вроде и все, что я хотел рассказать и показать.

Пишите комментарии, подписывайтесь на канал.

Всем пока.

=================================

Краткое содержание данного канала.

➖➖➖➖➖➖➖➖➖➖➖➖➖

Landingcentr.ru - разработка сайтов для малого и среднего бизнеса.

=================================