Эшу быдлокодит
310 subscribers
138 photos
12 videos
7 files
178 links
Дневник C# разработчика.

Личка: @EshuMarabo
Гитхаб: https://github.com/vladzvx

Стек: C#, PostgreSQL
Download Telegram
#klhztrader. Часть 2. Визуализация данных.

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

В шарпе все несколько сложнее. Изначально я хотел вкрячить какую-то визуализацию прямо в бота по принципу server-side rendering. Я потыкал несколько библиотек для построения графиков. Все они предназначены для серьезной разработки, но ведь моя цель - торговый бот, а не классный визуализатор данных!

Сунулся в React.js, с которым я капельку знаком - за два года там чего-то навертели в гайдах по быстрому старту, старт нифига не быстрый. Vue.js оказался сильно дружелюбнее. Но добрый человек @vekhden_speak посоветовал попробовать для визуализации графану (при попытке сложить данные в прометеус, так, чтобы было удобно, и родился один из прошлых постов). В графане я и остался.

В итоге я пришел к тому, что локально на ноуте кручу данные, укладываю результаты экспериментов в таблицу в постгресе и отсматриваю результаты работы на нескольких дашбордах в графане.
🔥4
#klhztrader. Часть 3. Инфраструктура.

Бот хостится на виртуалке, 4 х 2.2ГГц, 4Гб оперативки и HDD на 80 Гб, хостер - ruvds. Пока таких ресурсов хватает с головой.

На сервере в докер композе живут шарповый сервис бота, Prometheus, Loki, Graphana, и PostgreSQL. Для мониторинга состояния сервера в качестве обычного демона установлен прометеусовский node_exporter.

Для автодеплоя используется сервер gitea, на которой меня любезно пустил @ssleg. О моих сражениях с настройкой раннера я писал ранее.

Так как проектом занимаюсь я один, было решено в плане мониторинга и обработки логов остановиться на прометеусовском стеке. Я не очень люблю Loki, но для небольшого проекта он оказался идеальным: диск и оперативку потребляет по минимуму, без проблем стыкуется с графаной. Логов у меня не много: 4-5 записей в секунду, во основном меня интересуют ошибки, при том сортировать/фильтровать их не нужно. А тот же ELK съел бы у меня все ресурсы сервера, не дав ничего нового.

В итоге я собрал симпатичный дашборд для мониторинга состояния сервера. На нем четыре графика:
1. Оперативная память. Два графика - доступная память и потребление моего сервиса.
2. Утилизация CPU. По графику на каждое из ядер + отдельно график доли моего сервиса в общем потреблении.
3. Свободное место на диске.
4. Нагрузка на сеть.

Для экстренного управления сделан тетеграм бот. Он позволяет ребутнуть бота (Environment.Exit), бот вырубается, а затем средствами докера автоматически запускается. Ещё можно включить/выключить реальные покупки и продажи, а также докупить или сбросить активы на бирже.
🔥4👍3
#klhztrader. Часть 4. Архитектура приложения, некоторые особенности реализации.

Я подписываюсь на поток данных биржи (стрим grpc), после чего распространяю эти данные по своему приложению через каналы (Channel<T>), обернутые в абстракцию, чтобы при желании можно было без боли воткнуть туда брокер сообщений и разорвать бота на любое количество процессов.

История торгов за последние несколько часов нужна достаточно часто, тягать ее каждый раз из базы - не вариант. Значит - нужно кеширование. Redis/Tarantool я тащить не стал, сделал самописный кэш. Первые итерации были на основе массивов, но в итоге я пришел к кэшу на базе LinkedList (первый раз использовал его на практике).

Он оказался идеальным решением, чтобы держать в памяти данные за определенный промежуток времени. Добавляем новый элемент с помощью AddLast, а дальше в цикле while удаляем с помощью RemoveFirst всё вылезшее за пределы заданного временного интервала. Доступ к листу идёт через обычный lock. Для вычислений данные копируются в массив, который отдаётся запросившему.

Все используемые мной в проекте модели для хранения данных имеют поля с доступами {get;init;}, что защищает меня от чудес, которые могут проявиться при изменении объектов из нескольких потоков. Можно было бы упороться в readonly struct, но это уже для высоких нагрузок, которых у меня не предвидится.
👍2🔥1
#klhztrader. Часть 5. Некоторые биржевые понятия.

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

Позиция - любой актив, который есть у нас в наличии. Мы можем открыть (войти в) позицию - приобрести некоторое количество активов. Или закрыть (выйти из) - избавиться от нее, получив деньги.

Лонг - позиция, открытая в надежде на рост цены актива.

Шорт - позиция, открытая в надежде на падение цены актива.

Волатильность - нестабильность цены актива.

Брокер - прокладка между частным лицом и Мосбиржей, в моем случае - T-инвестиции.

Маржинальная торговля (маржиналка) - торговля с привлечением заёмных средств брокера. Прибыль или убыток начисляется два раза в рабочий день во время клиринга.

Клиринг - в районе 14 и 19 часов по будням биржа останавливает торги и проводит различные расчёты, в т.ч. начисляет или списывает маржу, которую заработал пользователь на фьючерсах с прошлого клиринга.

Фьючерс - производная от реально существующей ценной бумаги, по дефолту торгуется маржинально. По сути, торговля ведётся ожиданиями на цену.

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

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

Заявки - заявки на покупку или продажу актива. Лимитная заявка - заявка на покупку/продажу не более чем N ценных бумаг по цене M.

Совокупность лимитных заявок образует стакан. Назван он так из-за одной из самых популярных форм отображения, при случае приложу скрин. Заявки можно бесплатно выставлять и отменять сколько угодно раз, комиссия взимается только за исполнение.

Самый быстрый способ что-то купить/продать - сделать рыночную заявку. Если хватает лимитных заявок в стакане, она съедает их оттуда. Соответственно, чтобы сместить цену в какую-то сторону, нужно чтобы часть стакана была съедена рыночными заявками (а часть будет отменена пользователями и роботами). Если лимитных заявок в стакане не хватает на удовлетворение рыночной заявки - будет выброшена ошибка.
5👍3
#klhztrader. Часть 6. Выбор первого актива для торговли.

Основным активом, на торговле которым я пока сосредоточился, стал фьючерс на индекс Мосбиржи (IMOEXF). Индекс представляет собой расчетный актив (то есть в природе такого актива, привязанного к конкретной компании не существует).

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

Выбор обусловлен несколькими факторами, основной из которых - так сложились звёзды. Кроме того, у него нашлось ещё несколько преимуществ:
1. Всегда когда открыта биржа, кто-то им торгует.
2. У него интересное соотношение минимального шага цены к комиссии. Прибыль может принести практически любое шевеление цены, если поймаешь конечно:)
3. Как следствие п. 1 - в стакане зазор между максимальной ценой покупки и минимальной ценой продажи (спред) равен минимальному шагу цены, что упрощает логику торговли.
4. У него очень часто проявляется выраженная периодическая компонента. Я подумал: уж ее то я с преобладанием Фурье кааак выловлю!

Цена фьючерса представлена в пунктах (пт), 1 пт = 10 рублей.
🔥5
#klhztrader. Часть 7. Чудеса во взаимодействии с биржей.

Первое что я стал запрашивать - актуальные значения цен активов. Подавляющее большинство графиков с биржи представлено в виде красивеньких свечей. Биржа и брокер свободно отдают историю свечей любого размера, от 5 сек до суток, и есть даже метод для подписки на них. Конечно же я им воспользовался! Но оказалось, что есть нюанс: свечи приходят с задержкой в минуту и больше.

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

И только третья попытка оказалась пригодна для торговли в реальном времени: подписка сделки по активу.

Еще были увлекательные приключения с учётом активов. Два раза в рабочий день - около 14 и 19 часов происходит клиринг, в результате которого начисляется вариационная маржа на фьючерсы. Цена, относительно которой маржа будет начислена после закрытии позиции или во время следующего клиринга меняется, но в данных, отдаваемых биржей, не фигурирует. Я подписался на стрим моих операций, чтобы отлавливать момент входа/выхода из позиции, но вот проблема, брокер иногда отдаёт данные об открытии/закрытии позиции по два раза.

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

В итоге, учёт активов и отладка открытия закрытия стоили мне около 10 тыс потерь. То непередаваемое чувство, когда бот вовремя закрывает лонг, всё отлично. Но случайно открываются два шорта, которые тебя обилечивают рублей на 500-600 за несколько минут. Я отказался от самописного учёта активов, есть единственный источник истины - биржа. И сделал всё взаимодействие с биржей строго однопоточным в рамках каждого из счетов, через блокировку. Запрос, ответ, синхронизация портфеля, пропускаем следующий запрос. А проблему переоценки фьючерсов во время клиринга я решил радикально: за несколько минут до него я тупо сбрасываю все фьючерсы вне зависимости от цены. И судя по тому, как периодически колбасит цену за несколько минут до клиринга, так делает не только мой бот:)
👍5😁1
#klhztrader. Часть 8. Первая торговая стратегия.

Где-то 27 августа первая реализация бота была готова, весь основой костяк: взаимодействие с базой, взаимодействие с биржей, вывод лога сделок и минимальное управление из телеграмма. И первая, самая примитивная стратегия, я ее обозвал локальные тренды.

Я даже не рассчитывал, что она принесёт какую-то прибыль, задача была отладить всю остальную часть бота, после чего уже сосредоточиться на разработке стратегии торговли.

Суть стратегии следующая: берутся ~2 последние минуты истории торгов, делятся на два интервала: (-120,-10] и (-10,0] секунд, которые аппроксимируются прямыми. В зависимости от взаимного направления прямых открывается шорт или лонг. Критерий значимости точки - небольшое изменение цены в левой прямой (-1,+1) и резкое - в правой <-1.5 или >1.5.

В течение пары часов бот торганул на 600р в плюс, потом на 800р в минус, после чего был загашен до релиза следующей стратегии.
👍4😁3
#klhztrader. Часть 9. Стратегия скользящих средних.

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

Если было пересечение, после которого среднее по меньшему окну стало отрываться от среднего по бОльшему - пора входить, в лонг или в шорт - зависит от направления отрыва. Если средние пересеклись, образовав ощутимую ступеньку - пора выходить. Все очень просто, красиво, но на дистанции не работает:)

В итоге, около месяца было потрачено на безуспешные попытки вывести эту стратегию в плюс. Дальше будут описаны несколько костылей, которые я перепробовал и которые были полезны с т.з. саморазвития.
😁4👍2
#klhztrader. Часть 10.1. Вводная про преобразование Фурье.

Самые увлекательные приключения у меня были с преобразованием Фурье.

Преобразование Фурье, если совсем просто, это представление сигнала в виде набора периодических функций с разными частотами. Было у нас по оси x время, а по оси y - значение. Выполнили прямое преобразование Фурье - получили по оси x - частоту, по оси y - амплитуду периодической функции. Одно из названий результата - спектр. Выполнили обратное преобразование - получили исходный сигнал. Если к спектру применить какую-то трансформирующую функцию (фильтр), то после обратного преобразования сигнал изменится. Например, если обнулить высокие частоты - сигнал почистится от шумов. Довольно обыденная вещь, владеть которой должен любой инженер.

Но есть некоторые нюансы, которые я изложу ниже. Самое первое, что поджидает бодрую личинку инженера, сдавшую курс матана и, возможно, поигравшуюся со спектроанализатором, при попытке использовать готовое преобразование Фурье из любого из математических пакетов - спектр НЕ ТОТ.

Реализованное во всех математических пакетах быстрое дискретное преобразование Фурье (FFT) выдаёт спектр ощутимо отличающийся от привычного. В привычном случае у нас ось частот (x) идёт слева направо, мы рисуем ее пересечение с осью амплитуд частотных компонент в нуле и радуемся жизни. В результате быстрого дискретного преобразования осей частот по факту две. Одна идёт с начала массива, содержащего результат преобразования и занимает его первую половину. По ней идут положительные частоты. Вторая начинается в конце массива, и идет к середине, соответствует отрицательным частотам и занимает вторую половину массива с результатом преобразования. Наглядную иллюстрацию выложу в следующем посте.

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

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

P.S. Раньше я касался преобразования фурье в далёком 2021 году, когда ещё не забросил диссертацию.
🔥3
Иллюстрация к предыдущему посту. На верхней части рисунка - классическое представление спектра, в том виде, в котором он предстаёт на экранах спектроанализаторов и в котором осознается на парах матана.

А на нижней части я извратил исходный график до вида, в котором он выдаётся из FFT.
🔥5
#klhztrader. Часть 10.2. Попытки применения преобразования Фурье в торговле.

Первое что бросается в глаза при просмотре графика цен бумаг, которыми активно торгуют (например IMOEXF) - некая периодичность, когда рост сменяется падением и так по кругу. Я попробовал применить преобразование Фурье для решения задачи "Куда не может пойти IMOEXF в ближайшее время?".

Алгоритм решения задачи примерно такой:
1. С помощью пакета MathNet.Numerics выполняем преобразование Фурье.
2. Выбрасываем из спектра низкочастотные и высокочастотные компоненты (оставляем диапазон [60, 2] минут).
3. Рассчитываем обратное преобразование для полученного спектра на основе суммы косинусов фаз гармоник, умноженных на их амплитуду.
4. Рассчитываем обратное преобразование для производных гармоник спектра на основе суммы синусов фаз гармоник, умноженных на их амплитуду, от него оставляем только знак, чтобы понимать, мы сейчас растем или убываем.
5. Проверяем к какой части относится последняя точка - текущее время.

На рисунке ниже приведены некоторые ответы, который мне давало преобразование Фурье:
1. Если последняя точка попадает в первый дециль (фиолетовые точки) и имеет положительный знак - расти уже некуда, но предпосылок для падения пока нет.
2. Если последняя точка ниже медианы (светло-красные точки) и знак отрицательный - то в ближайшее время график будет убывать.
3. Если последняя точка в нижнем дециле (ярко-красные точки) и знак отрицательный - то убывать уже некуда, но предпосылок для роста не наблюдается.


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

Соответственно, пока место у преобразования Фурье графика цены - на свалке в архиве, а единственная (при том дальняя) перспектива - максимизация профита, чтобы входить-выходить из позиции в нужную фазу колебания, добавляя 2-3 пункта к результату. Я отложил его до лучших времен и двинулся к следующей стратегии, о которой расскажу отдельно.
🔥5
price - зеленый график - цена IMOEXF
fft_lower - светло-красные точки - места, где график скорее всего будет только убывать.
fft_lower_10 - ярко-красные точки - места, где графику убывать некуда, но расти пока тоже не будет.
fft_upper_10 - фиолетовые точки - места, где графику расти некуда, но убывать пока тоже не будет.
👍1👏1
Все свои 7.5 лет в айти я забивал насколько мог на форматирование sql кода. Маленькими буквами? Или капсом? Правильные отступы? Да нахрен надо!

И вот настал день: передо мной простыня sql кода на 7 тыс строк. При том на оракловом диалекте sql который я не знаю от слова совсем.

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

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

А будь там sql в моем стиле - я угрохал бы пару дней как минимум.
🔥13👍1
#klhztrader. Часть 11. Уровни поддержки и сопротивления.

Одной из попыток оптимизировать стратегии стала реализация уровней поддержки. Уровень поддержки в трейдинге - это воображаемая линия (или диапазон), из которого трудно выбраться (или пробить) цене. Выглядит как псевдофизический бред, но тем не менее в некоторых случаях работает.

Каким путем я пошёл:
1. Вычисляется гистограмма графика цены за определенный период времени (у меня это 2-3 часа).
2. Выполняется свертка гистограммы с ядерной функцией - параболой. В оригинале результат свертки - что-то вроде интеграла произведения двух графиков. Мне лучше подошло стандартное отклонение между гистограммой и ядром, умноженным на максимальное значение гистограммы в области пересечения.
3. Выделяются пики в гистограмме, берутся первые 4. Если пики накладываются друг на друга - они объединяются в один.

Полученные границы выделенных я объявил уровнями поддержки/сопротивления.

Мне они в итоге не особо помогли, но решение проблемы выявления пиков в гистограмме вполне достойно, чтобы её описать, а спустя N лет при необходимости вернуться и освежить память: подобные задачи мне попадались неоднократно.
🔥4
#klhztrader. Часть 12. Новая стратегия входа на базе дивергенции.

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

От друга-трейдера я слышал волшебное слово: "дивергенция" и решил попробовать ее реализовать. Дивергенция в трейдинге - один из разворотных паттернов. Пример дивергенции приложу в следующем посте. Когда происходит бодрый рост объемов торгов, как правило происходит не менее бодрое движение цены. Зачастую рост объемов торгов продолжается, а цена сначала останавливается, а потом вообще разворачивается. Именно это расхождение движения цены и объема я и пытался засечь.

В качестве основы я взял изменение цены и объема торгов за 3 минуты (интервал подобран эмпирически). А дальше вычислил коэффициент корреляции Пирсона. Изначально я думал, что дело в шляпе: если нет корреляции (Пирсон около нуля), то вот она, дивергенция, можно закупаться/продавать. Но нет. И если входы ещё более-менее удавались (как мне казалось на глазок), то выходы не получились вообще. С этого момента стратегии входов и выходов разошлись.

После очередного обсуждения выяснилось, что на выбранной мной бумаге - IMOEXF - дивергенции в явном виде не наблюдается никогда. Для торговли "руками" используется график самого индекса мосбиржи, но апи данные по нему не отдаёт. В итоге после пары недель экспериментов я таки пристроил коэффициент корреляции Пирсона к делу: дивергенция (или что-то по мотивам) таки работает на IMOEXF.

Итоговая база актуальной стратегии входов выглядит так: если коэффициент корреляции Пирсона пересекает значение 0.3 и ближайший экстремум графика коэффициента корреляции имеет ощутимое значение (0.6 и выше), а дифференциал цены за последние три минуты положителен - это потенциальная точка входа в лонг. Для шорта всё то же самое, только с обратным знаком.
🔥2🤔2
В качестве примера дивергенции приведу вчерашний скрин из терминала (SBER). Верхний график - цена (в виде пятиминутных свечей). Нижний график (столбцы) - объемы торгов.

Невооруженным глазом видно, что падение цены замедлилось, а объемы торгов остались прежними. В итоге цена развернулась.
2👍1🔥1
#klhztrader. Часть 13. Метрики корректности работы бота.

При работе над дивергенцией я понял, что входы и выходы не обязательно должны быть как-то связаны. Раньше я придерживался примерно такой логики: есть точка потенциального выхода (пересечение скользящих средних или переход экстреммума в графике коэффициента корреляции Пирсона). Если в точке потенциального выхода выход принесёт профит - выходим. Нет - ждём дальше. К сожалению, получилась ерунда. После неудачного запуска стартовой стратегии на дивергенции (еще тысячи 3-4 в минус), я понял, что пора заниматься симуляцией реальной работы. Начались сотни прогонов всей доступной мне истории сделок (с 1 сентября).

Итогом прогонов стали следующие выводы:
1. Итоговая цифра на счету в конце прогона - и близко не целевая метрика при доработках стратегии.
2. Не столь важно, как ты бьёшь, важно какой удар ты держишь как ты входишь в позицию, гораздо важнее как выходишь, минимизируя потери и максимизируя прибыль.

В итоге я пришёл к следующим целевым метрикам:
1. Равномерное нарастание прибыли (график прибыли должен стремиться к прямой). Метрика качественная, оценивается на глаз по графику прибыли за всё время.
2. Отсутствие резких падений. Вариант сделали +50% депозита, а потом слили 25% получив +12.5% (1.5 * 0.75 = 1.125) не годится, лучше маленькими шажками набрать меньшую сумму. Также качественная метрика, "устраивает/не устраивает".
3. Основную метрику, на которую я ориентируюсь я обозвал для простоты КПД. Вычисляется она по простой формуле:

КПД = (margin_plus/(margin_plus + margin_minus + comission)) - 0.5
.

где margin_plus - плюсовая маржа, чистая прибыль. margin_minus - отрицательная маржа, следствие ошибок стратегии. comission - комиссия брокера. 0.5 я добавил для красоты визуализации, чтобы значение 0 соответствовало точке безубыточности.
4. Ну и разумеется я смотрю на итоговую цифру на счету, но это уже скорее для того, чтобы вдохновляться на дальнейшую работу. Еще хорошо вдохновляет прогон на больших суммах, когда брокер снижает комиссию за большие обороты и цифры потенциальной прибыли начинают греть душу.
🔥4
#klhztrader. Часть 14. Стратегия выходов.

Важное значение в трейдинге имеют стопы: границы, после пересечения которых начинается сброс позиций.
Стопы, вдохновлявшие меня:
1. stop-loss, остановка потерь, фиксация убытков. Срабатывает на определенном уровне.
2. take-profit - аналог stop-loss, только фиксируется прибыль.
3. trailing stop - при пробитии определенного уровня стоп-линия следует за акутальной ценой с некоторой задержкой. Когда рост кончается и происходит отскок, пробивающий отстающую от цены стоп-линию - происходит закрытие позиции.

Итого, я использующую примерно следующую стратегию выходов:
1. Обычные биржевые stop-loss и take-profit с широкими диапазонами (отступ 20 пунктов на stop loss и 80 на take profit) в качестве страховки от несрабатывания логики выходов.
2. Динамически меняющиеся "стопы", прописанные у меня в коде бота. Цена совершила резкое движение в нужную сторону - сдвигаем "стопы". Происходит что-то не то - сдвигаем стопы.
3. Банальная линейная аппроксимация. Если мы уже набрали какой-то профит, а линейная аппроксимация говорит, что минут через 15 все может сгореть - фиксируем прибыль. Если профита нет, но дело пахнет керосином цена летит туда куда не надо - фиксируем убытки и движемся дальше.
👍3
#klhztrader. Часть 15. Некоторые особенности внутрянки бота.

Изначально я писал по инерции красиво и энтерпрайзно, с кучей абстракций, короткими классами и декомпозицией на короткие методы. Я старался по возможности затаскивать в проект известные мне механики оптимизации производительности (например избегать лишних аллокаций с помощью readonly ref struct).

Где-то на третий месяц все рюшечки пошли лесом: сколько времени займёт прогон 80 дней истории торгов - 10 или 15 минут вообще без разницы, я пойду кофе попью. Главное чтобы не часы. В какой-то момент мне также надоело писать логику так, чтобы было красиво: пишем раскинутый на 10 файлов код 3-4 часа, делаем 10 минутный прогон. Прогон показывает, что был написан хлам, сжигающий всю прибыль и уводящий в глубокие минуса. Если не затупил с коммитами - можно просто сделать git restore . , а вот если затупил - приходится вычищать мусор руками, что съедает еще время.

Так что теперь у меня есть god object - класс под названием Trader, размером в 2.5 тысячи строк (он partial, потому разбит на 6 файлов, чтобы не сходить с ума листая его), в котором есть пара десятков словарей для кеширования каких-то данных, нужных при работе. Ну и есть отдельный кэш для данных биржи и внутренних событий бота. Весь проект - 8.5 тыс. строк.

Для упрощения кода я использую событийную модель: все обработчики генерируют события (небольшой класс, содержащий время, акутальную цену, тип и несколько цифер (value1, value2 и т.д.). После генерации событие логируется в базу для дальнейшей визуализации в графане и кладётся в кэш бота, откуда события за различные промежутки времени запрашиваются по мере необходимости. Пример визуализации событий я приложу в посте ниже.

Ранее я отказался от внутреннего учёта активов, но с началом постоянных прогонов на истории, мне пришлось написать заглушку на место биржи, где учитываются "активы". Теперь все данные сделок имеют флаг IsHistoricalData, в зависимости от которого иногда меняется логика их обработки: данные настоящие - подсовываем настоящую биржу, исторические - подсовываем заглушку.
🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
#klhztrader. Часть 16. Итоги года.

Разработка бота идёт 4.5 месяца, но что-то похожее на результат стало вырисовываться только с началом декабря. Рост потенциальной прибыльности по мере доработки стратегии можно посмотреть на гифке в посте. По оси х - время, по оси y - размер портфеля. Если на старте залогированной мной истории (1 сентября 2025 года) выдать боту 35 тыс рублей, то за 4 месяца он превратит их примерно в 66 тысяч, то есть прибыль около 13% в месяц, или 156% годовых.

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