Я не планировал писать вторую статью на эту тему, но получив от вас много вопросов и откликов, на предыдущий пост (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 мгновений весны». Если вы используете чьи-то сигналы при торговле, пусть вам расскажут алгоритмы этих сигналов, а лучше - делайте свои.

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

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

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