Создание ботов для торговли криптовалютами и акциями (часть вторая)
Я не планировал писать вторую статью на эту тему, но получив от вас много вопросов и откликов, на предыдущий пост (https://habr.com/ru/post/675092/) решил ответить сразу всем, закрыть эту тему и перейти к следующим, не связанным с торговлей.
Немного общих слов
Давайте разделим решения на стратегические и тактические. Стратегические - это когда вы думаете, что через год биткоин будет стоить 100 тысяч долларов, покупаете его и молитесь чтобы это было так. Это же относится и к акциям. Тактические решения - это когда вы покупаете сейчас актив и почти уверены, что сумеете продать его с прибылью в течение ближайших дней. С моей точки зрения стратегические решения в нашем мире невозможны, слишком сложный мир, а вот тактические - вполне. Хороший пример - это акции «Газпрома»: вроде бы и цена на газ большая и все хорошо, а вот решили не выплачивать дивиденды и все рухнуло. Возникает вопрос: как это можно было предвидеть, не имея инсайда внутри правительства? То же самое относится и к криптовалютам. Поэтому лично я не верю никаким долгосрочным прогнозам аналитиков.
Алгоритм бота
Правило “В цене все отражено”, с моей точки зрения, не совсем верно. Надо обязательно ориентироваться на объёмы. В основе алгоритма моего бота лежит построение скользящий гистограммы объёмов торгов за определенный промежуток времени, для крипты я использую интервал в два дня. Для акций на моделях лучший интервал был пять дней, наверное, это связано с рабочей неделей. Берем общий объём торгов за прошедшие 48 часов, считаем объём торгов, который укладывается в диапазон текущей цены, плюс минус дельта. У меня дельта равна 1% от текущей цены. Затем делим полученный объём на общий объём. Значение всегда находится в диапазоне 0 – 1, назовем это значение sup_line. Когда значение sup_line больше 0.5, и мы какое-то время (для крипты - 4 часа, для акций - 8 часов) ходим вокруг этой цены, считаем это линией поддержки. Если sup_line 1, то это значит, что никакой волатильности нет, если значение меньше 0.1, то вероятно или летим в бездну или в космос. Это основа, теперь накладываем тонкие мазки. Надо покупать, когда на рынке все побежали, то есть резко возрастает количество сделок. Сначала я считал сделки простым count (*) всех сделок за три часа. Затем понял, что это не очень корректно. Если кто-то решил купить по рынку на большую сумму, то мы получим кучу операций по разной цене, а интересует только количество настоящих сделок. Поэтому я стал использовать count(distinct t_time) где t_timе- это время операции. Все операции в один момент я считаю за одну сделку. Результат работы сразу улучшился. Если вы читали книгу "Воспоминания биржевого спекулянта", то помните, как главный герой говорил, что цена идет туда где меньше сопротивление, и как он проверял это? Спойлерить не буду, прочитайте книгу. Когда цена ходит вокруг линии поддержки я запоминаю максимум и минимум цены. Затем, если цена резко пошла, sup_line стал меньше 0.25 и продолжает уменьшаться, все побежали (резко увеличилось количество сделок), отклонение от линии поддержки вверх больше чем вниз, я покупаю. В теории можно держать актив до тех пор, пока не упремся в новую линию поддержки, то есть пока sup_line не станет больше 0.5. Но на практике лучше продать часть актива, когда вы получите устраивающий процент прибыли, поскольку возможно это был ложный пробой и мы свалимся вниз на старую линию поддержки. Я продаю половину, когда прибыль больше полтора процента и объёмы покупок начинают падать. После чего если это был ложный прорыв, и мы упали на старую линию поддержки, продаю вторую половину без убытка. Если все хорошо, то держу вторую половину до новой линии поддержки и как только мы отскакиваем вниз от новой линии поддержки, то продаю эту половину. Если купил актив, а цена пошла вниз, то, как только цена уходит ниже линии поддержки, и возрастают объёмы продаж, я продаю все в убыток, не надеясь на хороший исход. Перед тем как купить я еще смотрю корзину, где желающих больше на покупку или продажу. Это увеличивает количество успешных сделок, но, к сожалению, нельзя проверить на эмуляторе, поскольку невозможно получить историю состояния корзины, но даже без корзины эмулятор показывает неплохие результаты. Данный алгоритм я применяю ко всем парам криптовалют. Если задуматься, то этот алгоритм похож на свечной анализ, там ведь многие фигуры так же показывают линии поддержки и сопротивления, когда свечи придумали компьютеров, как вы знаете, еще не было.
Проблемы алгоритма
В крипте все альткоины следуют за битком. К сожалению, нет независимых альтов. В акциях проще, например, вниз ушло авиа, вверх пошла фарма, что случилось в пандемию. Поэтому с акциями работать проще, но тут все упирается в хороший апи бирж и законы. Если бот купил какой-то альткоин и он резко подскочил в верх, например на 25%, а в это время биткоин стоит на месте или пошел вниз, то я предпочитаю зафиксировать прибыль, так надежней. Пока я это делаю вручную, кнопками «продать», но думаю добавить эту функцию в алгоритм. Другая проблема состоит в большом количестве бирж. По-хорошему надо собирать, нормализовать и агрегировать данные с нескольких крупных бирж. В данный момент я работаю только на Бинансе, но есть альты, для которых Бинанс не является основной биржей, поэтому расчет линии поддержки не будет правильным для них. Для отсечки таких альтов я применяю простое правило: если меньше 600 сделок в час проходит с этими альтами, я ими не торгую. В будущем планирую сделать агрегацию с разных бирж.
Не кладем все яйца в одну корзину
Никогда не покупаю «на всю котлету». Я распределяю деньги на части. В результате, в начале торгов бот может купить несколько разных альткоинов, затем продать половину, о чем я писал выше и вернуть часть денег в пул, из которого я снова могу купить другие альты. Иногда у меня бывает до 8 разных альткоинов на руках. Для каждого альткоина я могу определить какую максимальную часть денег готов ему выделить. Для акций я бы еще ввел распределение по отраслям, чтобы не оказаться в момент падения цен на газ и нефть только в акциях «Газпрома» или «Лукойла».
Алгоритмы ботов, которые не получились
Когда я начинал создавать бота у меня было много идей, о двух интересных хотелось бы рассказать.
Первая мысль была: найти все точки перелома графика цены, назовем эти точки ТИ (точки интереса), и в этих точках собрать вектор различных значений, объём торгов, скорость изменения объёма торгов, скорость изменения цены и тому подобное, главное, чтобы значения были относительными. Набираем много таких векторов и затем берем вектор текущей точки и вычитаем из него вектор ТИ, и когда результат вычитания меньше определенного значения, решаем покупать или продавать в зависимости от ТИ. Я взял исторические данные, разделил их пополам и на одной половине собрал ТИ, а на другой протестировал с помощью эмулятора. Результат не сильно порадовал. Тут главное состоит в том, какие значения включать в вектор, и кроме этого, требуется много экспериментальных вычислений. Как мне кажется, идея была красивой, но довести ее до ума я не смог. Пришла новая идея, и я забросил вектора, хотя в конце уже даже стало получаться. Сейчас мне кажется идея с векторами попыткой догнать уходящий поезд, на основе прошлых данных предсказать будущее.
Вторая идея — это создание внутри арбитражного бота. Строим цепочки купли-продажи разных альткоинов так, чтобы на выходе был плюс. Эта идея лежит на поверхности и таких желающих много. Я реализовал построение таких цепочек, и в теории это могло бы гарантированно приносить от 100 до 500 долларов в день, но это - в теории... На практике бот не успевал сделать все операции в цепочке, окно возможностей быстро закрывалось, и я получал убыток. Удалось провести успешно только 1 сделку из 5. Еще важна длина цепочки: чем она длиннее, тем больше возможностей, но и время расчета цепочки растет экспоненциально. Здесь привожу код построения цепочки, просто хочется похвастаться и обратить внимание на то, что pgsql - отличный инструмент для ботов.
CREATE OR REPLACE FUNCTION crypto_bot2.kraken_get_best_chain(
cur_code text,
cur_vol double precision,
min_profit double precision,
min_vol double precision)
RETURNS TABLE(chain text, cur text, len integer, profit double precision, vol double precision)
LANGUAGE 'sql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000
AS $BODY$
WITH RECURSIVE chains AS (
WITH ticker AS (SELECT * FROM crypto_bot2.kraken_ticker_table())
SELECT format('%s:%s', pair, CASE WHEN quote = cur_code THEN 'B:' || ask_price ELSE 'S:' || bid_price END) as chain,
format('%s->%s', cur_code, CASE WHEN quote = cur_code THEN base ELSE quote END) cur, quote, base, 1 len,
CASE WHEN quote = cur_code THEN 1 ELSE 0 END x, 0 s,
CASE WHEN quote = cur_code THEN 100 / ask_price ELSE 100 * bid_price END * 0.9974 pr,
CASE WHEN quote = cur_code THEN least(cur_vol / ask_price, ask_vol / ask_price) ELSE least(cur_vol * bid_price, bid_vol * bid_price) END vol
FROM ticker WHERE cur_code IN (quote, base)
UNION
SELECT format('%s->%s:%s', c.chain, t.pair,
CASE WHEN c.x = 1 AND t.quote = c.base OR c.x = 0 AND t.quote = c.quote THEN 'B:' || t.ask_price ELSE 'S:' || t.bid_price END) ,
format('%s->%s', c.cur, CASE WHEN t.base IN (c.base, c.quote) THEN t.quote ELSE t.base END) ,
t.quote, t.base, c.len + 1,
CASE WHEN t.quote IN (c.quote, c.base) THEN 1 ELSE 0 END x,
CASE WHEN cur_code IN (t.quote, t.base) THEN 1 ELSE 0 END s,
CASE WHEN c.x = 1 AND t.quote = c.base OR c.x = 0 AND t.quote = c.quote THEN c.pr / t.ask_price ELSE c.pr * t.bid_price END * 0.9974 pr,
CASE WHEN c.x = 1 AND t.quote = c.base OR c.x = 0 AND t.quote = c.quote THEN least(c.vol / t.ask_price, t.ask_vol / t.ask_price) ELSE least(c.vol * t.bid_price, t.bid_vol * t.bid_price) END vol
FROM ticker t, chains c WHERE
( (c.x = 1 AND c.base IN (t.base,t.quote)) OR (c.x = 0 AND c.quote IN (t.base,t.quote)))
AND (position(t.base in c.cur) = 0 OR position(t.quote in c.cur) = 0 OR cur_code IN (t.quote, t.base))
AND s = 0 AND len <= 4
)
SELECT chain, cur, len, pr, CASE WHEN abs(cur_vol-least(vol,cur_vol))/vol < 0.001 THEN cur_vol ELSE vol END FROM chains
WHERE cur_code IN (quote, base) AND len >= 3 AND pr > (100 + min_profit) AND vol > min_vol ORDER BY vol DESC , pr DESC , len ASC LIMIT 1;
$BODY$;
Эмулятор
В предыдущей статье я писал, что одна из самых важных частей бота — это эмулятор. Бот сохраняет данные с биржи в таблицу такого вида
CREATE TABLE crypto_bot2.binance_trades
(
trade_id bigint NOT NULL,
pair text NOT NULL,
t_date timestamp with time zone NOT NULL,
price double precision,
volume double precision,
op_type text,
CONSTRAINT binance_trades_pkey PRIMARY KEY (trade_id, pair)
)
Затем я агрегирую эти данные, рассчитываю нужные значения и записываю в таблицу с данными, в таблице с данными идет агрегация по 1 минуте, тем самым данные сжимаются, и я могу их сохранять на большом диапазоне времени, а в таблице trades у меня хранятся данные только за последнею неделю, лишние данные я убираю в момент перезапуска бота. Эмулятор проходит по таблице с данными и показывает в каких точках он покупает и в каких продает, цену покупки и продажи эмулятор берет из цены худшей цены следующей минуты, то есть для покупки это максимальная цена следующей минуты, а для продажи - минимальная. Если у меня работает бот, то он берет только последнею строку в таблице с данными и принимает решение что ему делать и еще заглядывает в корзину, то есть эмулятор и бот работают с одними и теми же данными.
Заключение
Возвращаясь к предыдущей статье, я хотел показать, как удобно использовать pgsql для создания ботов. Я знаю много языков программирования, единственное чего я не понимаю -это зачем нужен Haskell, но это, наверное, потому что я в свое время не закончил 239 школу города Ленинграда. Лозунг «писать на том что лучше знаешь» мне как раз не нравится, и я хотел показать хороший инструмент для ботов, но не получилось, поэтому ставлю себе неуд за прошлую статью.
Почему я не использую плечи: потому что страшно, и мне кажется, что на рынке крипты — это нечестный инструмент. Крипто биржи никем не регулируются и они, выдавая вам большой кредит, запросто могут его у вас отобрать со всеми вашими деньгами, немного подвинув цены. Хотя возможно мое мнение ошибочно, во всяком случае монеты down я использую.
Если вы спросите «если все так хорошо работает, сиди и молчи в тряпочку», то, во-первых, есть такой грех как тщеславие. Наверное, многие смотрели фильм «Плутовство» («Хвост виляет собакой») и помните его конец, если не смотрели, то посмотрите, хороший фильм. Во-вторых, я сделал то, что нельзя было делать. Когда я сделал бота я взял кредит в банке, довольно большой, и бот успешно торговал, но надоело мне платить кредит, и я решил все и сразу, когда стала падать луна, я решил половить ножи ручками, слил практически все. Была большая печалька, но я хоть попробовал... Другим советовать не буду. Это событие также побудило написать меня этот пост.
Есть такой хороший сайт tradingview, там удобные графики и иногда интересно почитать, но в целом, мне кажется там пишут гадалки. Куда пойдет цена предсказать нельзя на долгий срок. «Верить никому нельзя, себе нельзя, мне – можно», как говорил один из известных персонажей в фильме «17 мгновений весны». Если вы используете чьи-то сигналы при торговле, пусть вам расскажут алгоритмы этих сигналов, а лучше - делайте свои.
Если торгуете акциями, то мой совет не вкладывать все в российские акции, тот фокус, что провернули с дивидендами «Газпрома» и «Сбербанка» вряд ли возможен на свободном рынке. Поэтому, кроме деления по отраслям акции еще неплохо делить их по рынкам.
Еще у меня есть идея для стартапа: создать магазин ботов, в котором людям дать удобный инструмент для создания ботов, затем эти боты могут работать на хозяина, а хозяин может выставить их в магазин. В магазине вместо рейтинга бота показывалась бы его настоящая результативность, за этим бы следила бы платформа и накрутить результативность было бы невозможно.
Мои контакты вы можете найти в прошлой статье, она мне кажется более интересной. Если есть что сказать пишите в комментариях или мне в телеграмм. Улыбочки в посте не ставлю, поскольку это не приветствуется, улыбнитесь сами где сочтете нужным.