Последние двое выходных я провёл на Волге чуть выше Твери. Там уже нет судоходства, количество лодочников приемлемое, более менее адекватные подъезды к воде и должна быть рыба.
Но далась рыба далеко не сразу: прошлые раза четыре я благополучно пролетел. В этот раз дети очень хотели на рыбалку, а сил организовывать что-то увлекательное с лодкой у меня не было. Потому поехали просто посидеть с фидером на берегу, первый раз с детьми, второй - с другом.
К выбору точки ловли я впервые за долгие годы подошёл "по науке" от рыболовных блогеров: эхолотом нашел в воде место с перепадом глубин и обе рыбалки бомбил чисто его. Результат на фото: с каждой рыбалки привезли по три килограмма подлещиков.
#рыбалка
#волга
Но далась рыба далеко не сразу: прошлые раза четыре я благополучно пролетел. В этот раз дети очень хотели на рыбалку, а сил организовывать что-то увлекательное с лодкой у меня не было. Потому поехали просто посидеть с фидером на берегу, первый раз с детьми, второй - с другом.
К выбору точки ловли я впервые за долгие годы подошёл "по науке" от рыболовных блогеров: эхолотом нашел в воде место с перепадом глубин и обе рыбалки бомбил чисто его. Результат на фото: с каждой рыбалки привезли по три килограмма подлещиков.
#рыбалка
#волга
👏8👍5🔥3❤1
#klhztrader. Часть -1. Метрики.
Небольшая заметка на полях о прометеусе. Оставлю тут, чтобы не утратить обретенные знания.
Раньше я многократно пользовался Прометеусом для отображения метрик приложения. Рантайм шарпа по дефолту экспортирует достаточно информативный набор метрик, в т.ч. потребляемая приложением память, в т.ч. с разбивкой по поколениям сборки мусора. Добавлять кастомные метрики, позволяющие мониторить различные аспекты разрабатываемого приложения - более менее стандартная практика. И тут я задался вопросом, а как разделить метрики на группы (смапленные на разные пути: /metrics, /metrics/critical и т.д.), чтобы можно было опрашивать более критичные метрики с большей периодичностью.
Промучавшись некоторое время, я пришёл к следующему решению:
1. Основную группу метрик маппить стандартным способом, дефолтным .MapMetrics() в Program.cs
2. Для своих метрик создать свой CollectorRegistry, дальше методом Metrics.WithCustomRegistry создать экзмепляр фабрики MetricFactory, который заинжектить в приложение.
3. В местах, где требуются нестандартные метрики забирать MetricFactory и с его помощью создавать экземпляры метрик в нужных местах.
4. Отдачу своих метрик в прометеус осуществлять через самописный контроллер, в который инжектится CollectorRegistry. Дальше метрики в качестве text/plain отдаются контроллером.
#prometheus
Небольшая заметка на полях о прометеусе. Оставлю тут, чтобы не утратить обретенные знания.
Раньше я многократно пользовался Прометеусом для отображения метрик приложения. Рантайм шарпа по дефолту экспортирует достаточно информативный набор метрик, в т.ч. потребляемая приложением память, в т.ч. с разбивкой по поколениям сборки мусора. Добавлять кастомные метрики, позволяющие мониторить различные аспекты разрабатываемого приложения - более менее стандартная практика. И тут я задался вопросом, а как разделить метрики на группы (смапленные на разные пути: /metrics, /metrics/critical и т.д.), чтобы можно было опрашивать более критичные метрики с большей периодичностью.
Промучавшись некоторое время, я пришёл к следующему решению:
1. Основную группу метрик маппить стандартным способом, дефолтным .MapMetrics() в Program.cs
2. Для своих метрик создать свой CollectorRegistry, дальше методом Metrics.WithCustomRegistry создать экзмепляр фабрики MetricFactory, который заинжектить в приложение.
3. В местах, где требуются нестандартные метрики забирать MetricFactory и с его помощью создавать экземпляры метрик в нужных местах.
4. Отдачу своих метрик в прометеус осуществлять через самописный контроллер, в который инжектится CollectorRegistry. Дальше метрики в качестве text/plain отдаются контроллером.
#prometheus
👍3❤1
#klhztrader. Часть 0. Автодеплой проекта.
Запишу инструкцию по настройке основы самого простого автодеплоя с помощью gitea actions. Она основана на документации, но обходит несколько подводных камней на которые я наткнулся в процессе.
Цель: исполнять bash - команды на удаленном сервере по коммиту в репозиторий, раннер крутится как демон, не в контейнере. При этом, для деплоя нам скорее всего потребуется установленный на сервер докер. Работу без докера я не проверял.
1. Качаем бинарник gitea actions runner с сайта на сервер (для совместимости с доступной мне версией gitea я взял раннер двухлетней давности).
2. Переименовываем скачанный файл в act_runner и разрешаем ему исполняться:
3. Добавляем пользователя act_runner, я выдал ему sudo привилегии.
4. Запускаем ./act_runner register и идём по шагам, вставляя по запросу url gitea, registration token из настроек gitea (settings => actions => runners => Create new Runner), как-то называем раннер, затем нас спрашивают добавить labels для активации. Добавляем
Это указывает runner-у выполнять джобы, помеченные как
5. Убеждаемся в интерфейсе gitea - появился ли там созданный раннер.
6. Настраиваем запуск раннера в качестве демона как указано в документации. Ниже приведу мой вариант .service файла, в котором убрано использование конфига и перебиты пути на домашнюю директорию моего пользователя:
Дальше как в инструкции:
```shell
sudo systemctl enable act_runner --now
on:
- push
jobs:
test:
runs-on: my_project_deploy_cmd
name: test action
steps:
- name: test
run: echo "Hello from Gitea Action11s!" && docker ps -a
Важно, чтобы в runs-on был указан label, который мы задали в конце шага 4. Будет выполнен вывод текста в командную строку, а затем - запрос вывода статусов всех докер-контейнеров на сервере.
9. Имея в распоряжении командную строку, мы можем проделать любые манипуляции с сервером и завести деплой в том виде, в котором нам удобно.
#gitea
#devops
Запишу инструкцию по настройке основы самого простого автодеплоя с помощью gitea actions. Она основана на документации, но обходит несколько подводных камней на которые я наткнулся в процессе.
Цель: исполнять bash - команды на удаленном сервере по коммиту в репозиторий, раннер крутится как демон, не в контейнере. При этом, для деплоя нам скорее всего потребуется установленный на сервер докер. Работу без докера я не проверял.
1. Качаем бинарник gitea actions runner с сайта на сервер (для совместимости с доступной мне версией gitea я взял раннер двухлетней давности).
wget https://dl.gitea.com/act_runner/0.2.3/act_runner-0.2.3-linux-amd64
2. Переименовываем скачанный файл в act_runner и разрешаем ему исполняться:
chmod +x act_runner3. Добавляем пользователя act_runner, я выдал ему sudo привилегии.
4. Запускаем ./act_runner register и идём по шагам, вставляя по запросу url gitea, registration token из настроек gitea (settings => actions => runners => Create new Runner), как-то называем раннер, затем нас спрашивают добавить labels для активации. Добавляем
my_project_deploy_cmd:host Это указывает runner-у выполнять джобы, помеченные как
my_project_deploy_cmd непосредственно на сервере, а не внутри контейнера.5. Убеждаемся в интерфейсе gitea - появился ли там созданный раннер.
6. Настраиваем запуск раннера в качестве демона как указано в документации. Ниже приведу мой вариант .service файла, в котором убрано использование конфига и перебиты пути на домашнюю директорию моего пользователя:
[Unit]
Description=Gitea Actions runner
Documentation=https://gitea.com/gitea/act_runner
After=docker.service
[Service]
ExecStart=/home/my_user/act_runner daemon
ExecReload=/bin/kill -s HUP $MAINPID
WorkingDirectory=/home/my_user
TimeoutSec=0
RestartSec=10
Restart=always
User=act_runner
[Install]
WantedBy=multi-user.target
Дальше как в инструкции:
sudo systemctl daemon-reload
```shell
sudo systemctl enable act_runner --now
name: test
7. Включаем enable actions в настройках репозитория.
8. Добавляем в корень репозитория папку .gitea, в неё - папку workflows, в нее - файл test.yaml со следующим содержимым:
on:
- push
jobs:
test:
runs-on: my_project_deploy_cmd
name: test action
steps:
- name: test
run: echo "Hello from Gitea Action11s!" && docker ps -a
`Важно, чтобы в runs-on был указан label, который мы задали в конце шага 4. Будет выполнен вывод текста в командную строку, а затем - запрос вывода статусов всех докер-контейнеров на сервере.
9. Имея в распоряжении командную строку, мы можем проделать любые манипуляции с сервером и завести деплой в том виде, в котором нам удобно.
#gitea
#devops
Gitea: Git with a cup of tea
act_runner
A runner for Gitea based on act.
👍6
#klhztrader, Часть 1. Анонс.
Последние два месяца я все свободное от работы время занимался новым проектом: ботом для торговли на Мосбирже. Предыдущие два поста про метрики и gitea были написаны на этапе подготовки к деплою mvp бота. Все дальнейшие посты, касающиеся торгового бота будут под тегом #klhztrader.
Как родился проект. Я давно хотел познакомиться с темой инвестиций, торговли на бирже и т.д., но не хватало когнитивных ресурсов. И тут за время отпуска я внезапно отдохнул так, что снова захотелось думать. В доступе есть доменный эксперт - друг много лет успешно торгует на Мосбирже. Было принято решение изучить возможности, представляемые биржей.
Немного поторговав руками я понял, что слишком азартен, надо много работать над собой. Зато при просмотре графиков котировок у меня возникают флешбеки из универа и с первой работы в IT: там мне довольно много приходилось иметь дело с временными рядами.
Обычно такие вещи пишут на питоне, но я в гробу его видал, а под c# нашлось отличное api от T-Bank-а, поставляемое в качестве пакета, подключаемого в проект. Указываешь токен доступа - и можно торговать.
Дальше будет длинный конспект процесса разработки и результаты экспериментов. Работы все ещё активно ведутся, не переключайтесь. Код пока не выкладываю, возможно выложу потом.
Последние два месяца я все свободное от работы время занимался новым проектом: ботом для торговли на Мосбирже. Предыдущие два поста про метрики и gitea были написаны на этапе подготовки к деплою mvp бота. Все дальнейшие посты, касающиеся торгового бота будут под тегом #klhztrader.
Как родился проект. Я давно хотел познакомиться с темой инвестиций, торговли на бирже и т.д., но не хватало когнитивных ресурсов. И тут за время отпуска я внезапно отдохнул так, что снова захотелось думать. В доступе есть доменный эксперт - друг много лет успешно торгует на Мосбирже. Было принято решение изучить возможности, представляемые биржей.
Немного поторговав руками я понял, что слишком азартен, надо много работать над собой. Зато при просмотре графиков котировок у меня возникают флешбеки из универа и с первой работы в IT: там мне довольно много приходилось иметь дело с временными рядами.
Обычно такие вещи пишут на питоне, но я в гробу его видал, а под c# нашлось отличное api от T-Bank-а, поставляемое в качестве пакета, подключаемого в проект. Указываешь токен доступа - и можно торговать.
Дальше будет длинный конспект процесса разработки и результаты экспериментов. Работы все ещё активно ведутся, не переключайтесь. Код пока не выкладываю, возможно выложу потом.
🔥13❤2
#klhztrader. Часть 2. Визуализация данных.
При работе с временными рядами требуется периодически визуализировать то, с чем ты работаешь, с нанесенными на график промежуточными результатами. В питоне для этих целей есть прекрасна библиотека matplotlib, с её помощью можно в пару строк сделать график.
В шарпе все несколько сложнее. Изначально я хотел вкрячить какую-то визуализацию прямо в бота по принципу server-side rendering. Я потыкал несколько библиотек для построения графиков. Все они предназначены для серьезной разработки, но ведь моя цель - торговый бот, а не классный визуализатор данных!
Сунулся в React.js, с которым я капельку знаком - за два года там чего-то навертели в гайдах по быстрому старту, старт нифига не быстрый. Vue.js оказался сильно дружелюбнее. Но добрый человек @vekhden_speak посоветовал попробовать для визуализации графану (при попытке сложить данные в прометеус, так, чтобы было удобно, и родился один из прошлых постов). В графане я и остался.
В итоге я пришел к тому, что локально на ноуте кручу данные, укладываю результаты экспериментов в таблицу в постгресе и отсматриваю результаты работы на нескольких дашбордах в графане.
При работе с временными рядами требуется периодически визуализировать то, с чем ты работаешь, с нанесенными на график промежуточными результатами. В питоне для этих целей есть прекрасна библиотека 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 х 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), бот вырубается, а затем средствами докера автоматически запускается. Ещё можно включить/выключить реальные покупки и продажи, а также докупить или сбросить активы на бирже.
👍3🔥3
#klhztrader. Часть 4. Архитектура приложения, некоторые особенности реализации.
Я подписываюсь на поток данных биржи (стрим grpc), после чего распространяю эти данные по своему приложению через каналы (Channel<T>), обернутые в абстракцию, чтобы при желании можно было без боли воткнуть туда брокер сообщений и разорвать бота на любое количество процессов.
История торгов за последние несколько часов нужна достаточно часто, тягать ее каждый раз из базы - не вариант. Значит - нужно кеширование. Redis/Tarantool я тащить не стал, сделал самописный кэш. Первые итерации были на основе массивов, но в итоге я пришел к кэшу на базе LinkedList (первый раз использовал его на практике).
Он оказался идеальным решением, чтобы держать в памяти данные за определенный промежуток времени. Добавляем новый элемент с помощью AddLast, а дальше в цикле while удаляем с помощью RemoveFirst всё вылезшее за пределы заданного временного интервала. Доступ к листу идёт через обычный lock. Для вычислений данные копируются в массив, который отдаётся запросившему.
Все используемые мной в проекте модели для хранения данных имеют поля с доступами {get;init;}, что защищает меня от чудес, которые могут проявиться при изменении объектов из нескольких потоков. Можно было бы упороться в readonly struct, но это уже для высоких нагрузок, которых у меня не предвидится.
Я подписываюсь на поток данных биржи (стрим grpc), после чего распространяю эти данные по своему приложению через каналы (Channel<T>), обернутые в абстракцию, чтобы при желании можно было без боли воткнуть туда брокер сообщений и разорвать бота на любое количество процессов.
История торгов за последние несколько часов нужна достаточно часто, тягать ее каждый раз из базы - не вариант. Значит - нужно кеширование. Redis/Tarantool я тащить не стал, сделал самописный кэш. Первые итерации были на основе массивов, но в итоге я пришел к кэшу на базе LinkedList (первый раз использовал его на практике).
Он оказался идеальным решением, чтобы держать в памяти данные за определенный промежуток времени. Добавляем новый элемент с помощью AddLast, а дальше в цикле while удаляем с помощью RemoveFirst всё вылезшее за пределы заданного временного интервала. Доступ к листу идёт через обычный lock. Для вычислений данные копируются в массив, который отдаётся запросившему.
Все используемые мной в проекте модели для хранения данных имеют поля с доступами {get;init;}, что защищает меня от чудес, которые могут проявиться при изменении объектов из нескольких потоков. Можно было бы упороться в readonly struct, но это уже для высоких нагрузок, которых у меня не предвидится.
👍2🔥1
#klhztrader. Часть 5. Некоторые биржевые понятия.
В дальнейшем при описании стратегий торговли я буду оперировать некоторыми специфическими понятиями. Вообще, они легко гуглятся, но думаю не лишним будет вкратце описать их тут.
Позиция - любой актив, который есть у нас в наличии. Мы можем открыть (войти в) позицию - приобрести некоторое количество активов. Или закрыть (выйти из) - избавиться от нее, получив деньги.
Лонг - позиция, открытая в надежде на рост цены актива.
Шорт - позиция, открытая в надежде на падение цены актива.
Волатильность - нестабильность цены актива.
Брокер - прокладка между частным лицом и Мосбиржей, в моем случае - T-инвестиции.
Маржинальная торговля (маржиналка) - торговля с привлечением заёмных средств брокера. Прибыль или убыток начисляется два раза в рабочий день во время клиринга.
Клиринг - в районе 14 и 19 часов по будням биржа останавливает торги и проводит различные расчёты, в т.ч. начисляет или списывает маржу, которую заработал пользователь на фьючерсах с прошлого клиринга.
Фьючерс - производная от реально существующей ценной бумаги, по дефолту торгуется маржинально. По сути, торговля ведётся ожиданиями на цену.
Свеча - формат представления данных о торгах за временной интервал, включает в себя цену на начало, цену на конец, максимальную и минимальную цены.
Стопы - механизмы автоматического выхода из позиций при выполнении определенных условий. Это отдельная тема, о ней будет рассказано потом.
Заявки - заявки на покупку или продажу актива. Лимитная заявка - заявка на покупку/продажу не более чем N ценных бумаг по цене M.
Совокупность лимитных заявок образует стакан. Назван он так из-за одной из самых популярных форм отображения, при случае приложу скрин. Заявки можно бесплатно выставлять и отменять сколько угодно раз, комиссия взимается только за исполнение.
Самый быстрый способ что-то купить/продать - сделать рыночную заявку. Если хватает лимитных заявок в стакане, она съедает их оттуда. Соответственно, чтобы сместить цену в какую-то сторону, нужно чтобы часть стакана была съедена рыночными заявками (а часть будет отменена пользователями и роботами). Если лимитных заявок в стакане не хватает на удовлетворение рыночной заявки - будет выброшена ошибка.
В дальнейшем при описании стратегий торговли я буду оперировать некоторыми специфическими понятиями. Вообще, они легко гуглятся, но думаю не лишним будет вкратце описать их тут.
Позиция - любой актив, который есть у нас в наличии. Мы можем открыть (войти в) позицию - приобрести некоторое количество активов. Или закрыть (выйти из) - избавиться от нее, получив деньги.
Лонг - позиция, открытая в надежде на рост цены актива.
Шорт - позиция, открытая в надежде на падение цены актива.
Волатильность - нестабильность цены актива.
Брокер - прокладка между частным лицом и Мосбиржей, в моем случае - T-инвестиции.
Маржинальная торговля (маржиналка) - торговля с привлечением заёмных средств брокера. Прибыль или убыток начисляется два раза в рабочий день во время клиринга.
Клиринг - в районе 14 и 19 часов по будням биржа останавливает торги и проводит различные расчёты, в т.ч. начисляет или списывает маржу, которую заработал пользователь на фьючерсах с прошлого клиринга.
Фьючерс - производная от реально существующей ценной бумаги, по дефолту торгуется маржинально. По сути, торговля ведётся ожиданиями на цену.
Свеча - формат представления данных о торгах за временной интервал, включает в себя цену на начало, цену на конец, максимальную и минимальную цены.
Стопы - механизмы автоматического выхода из позиций при выполнении определенных условий. Это отдельная тема, о ней будет рассказано потом.
Заявки - заявки на покупку или продажу актива. Лимитная заявка - заявка на покупку/продажу не более чем N ценных бумаг по цене M.
Совокупность лимитных заявок образует стакан. Назван он так из-за одной из самых популярных форм отображения, при случае приложу скрин. Заявки можно бесплатно выставлять и отменять сколько угодно раз, комиссия взимается только за исполнение.
Самый быстрый способ что-то купить/продать - сделать рыночную заявку. Если хватает лимитных заявок в стакане, она съедает их оттуда. Соответственно, чтобы сместить цену в какую-то сторону, нужно чтобы часть стакана была съедена рыночными заявками (а часть будет отменена пользователями и роботами). Если лимитных заявок в стакане не хватает на удовлетворение рыночной заявки - будет выброшена ошибка.
❤5👍3
#klhztrader. Часть 6. Выбор первого актива для торговли.
Основным активом, на торговле которым я пока сосредоточился, стал фьючерс на индекс Мосбиржи (IMOEXF). Индекс представляет собой расчетный актив (то есть в природе такого актива, привязанного к конкретной компании не существует).
В индекс вносят вклад ценные бумаги крупнейших компаний российского рынка: Сбер, Газпром, Лукойл и т.д. По большому счету, он является индикатором состояния российского рынка в целом. Торгую я фьючерсом на него, т.е. ожиданиями на цену несуществующей фигни:)
Выбор обусловлен несколькими факторами, основной из которых - так сложились звёзды. Кроме того, у него нашлось ещё несколько преимуществ:
1. Всегда когда открыта биржа, кто-то им торгует.
2. У него интересное соотношение минимального шага цены к комиссии. Прибыль может принести практически любое шевеление цены, если поймаешь конечно:)
3. Как следствие п. 1 - в стакане зазор между максимальной ценой покупки и минимальной ценой продажи (спред) равен минимальному шагу цены, что упрощает логику торговли.
4. У него очень часто проявляется выраженная периодическая компонента. Я подумал: уж ее то я с преобладанием Фурье кааак выловлю!
Цена фьючерса представлена в пунктах (пт), 1 пт = 10 рублей.
Основным активом, на торговле которым я пока сосредоточился, стал фьючерс на индекс Мосбиржи (IMOEXF). Индекс представляет собой расчетный актив (то есть в природе такого актива, привязанного к конкретной компании не существует).
В индекс вносят вклад ценные бумаги крупнейших компаний российского рынка: Сбер, Газпром, Лукойл и т.д. По большому счету, он является индикатором состояния российского рынка в целом. Торгую я фьючерсом на него, т.е. ожиданиями на цену несуществующей фигни:)
Выбор обусловлен несколькими факторами, основной из которых - так сложились звёзды. Кроме того, у него нашлось ещё несколько преимуществ:
1. Всегда когда открыта биржа, кто-то им торгует.
2. У него интересное соотношение минимального шага цены к комиссии. Прибыль может принести практически любое шевеление цены, если поймаешь конечно:)
3. Как следствие п. 1 - в стакане зазор между максимальной ценой покупки и минимальной ценой продажи (спред) равен минимальному шагу цены, что упрощает логику торговли.
4. У него очень часто проявляется выраженная периодическая компонента. Я подумал: уж ее то я с преобладанием Фурье кааак выловлю!
Цена фьючерса представлена в пунктах (пт), 1 пт = 10 рублей.
🔥5
#klhztrader. Часть 7. Чудеса во взаимодействии с биржей.
Первое что я стал запрашивать - актуальные значения цен активов. Подавляющее большинство графиков с биржи представлено в виде красивеньких свечей. Биржа и брокер свободно отдают историю свечей любого размера, от 5 сек до суток, и есть даже метод для подписки на них. Конечно же я им воспользовался! Но оказалось, что есть нюанс: свечи приходят с задержкой в минуту и больше.
Ок, движемся дальше. Есть возможность подписаться на последнюю цену актива, я так и сделал. И все удивлялся, почему у меня в графане на графике в данных здоровенные дыры, до пары минут, а в терминале в T-Инвестициях дыр нет. В общем, данные оказались дырявыми.
И только третья попытка оказалась пригодна для торговли в реальном времени: подписка сделки по активу.
Еще были увлекательные приключения с учётом активов. Два раза в рабочий день - около 14 и 19 часов происходит клиринг, в результате которого начисляется вариационная маржа на фьючерсы. Цена, относительно которой маржа будет начислена после закрытии позиции или во время следующего клиринга меняется, но в данных, отдаваемых биржей, не фигурирует. Я подписался на стрим моих операций, чтобы отлавливать момент входа/выхода из позиции, но вот проблема, брокер иногда отдаёт данные об открытии/закрытии позиции по два раза.
Но самым эпичным было неочевидное поведение флага enable margin (разрешить маржинальную торговлю) при открытии/закрытии позиции. В конечном итоге, я так и не понял, как он работает. Даже с этим флагом никто не мешал боту в результате ошибок случайно открывать шорты на фьючерс мосбиржи (IMOEXF), хотя логично было бы не давать с выключенным флагом торговать этим активом вообще.
В итоге, учёт активов и отладка открытия закрытия стоили мне около 10 тыс потерь. То непередаваемое чувство, когда бот вовремя закрывает лонг, всё отлично. Но случайно открываются два шорта, которые тебя обилечивают рублей на 500-600 за несколько минут. Я отказался от самописного учёта активов, есть единственный источник истины - биржа. И сделал всё взаимодействие с биржей строго однопоточным в рамках каждого из счетов, через блокировку. Запрос, ответ, синхронизация портфеля, пропускаем следующий запрос. А проблему переоценки фьючерсов во время клиринга я решил радикально: за несколько минут до него я тупо сбрасываю все фьючерсы вне зависимости от цены. И судя по тому, как периодически колбасит цену за несколько минут до клиринга, так делает не только мой бот:)
Первое что я стал запрашивать - актуальные значения цен активов. Подавляющее большинство графиков с биржи представлено в виде красивеньких свечей. Биржа и брокер свободно отдают историю свечей любого размера, от 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р в минус, после чего был загашен до релиза следующей стратегии.
Где-то 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 году, когда ещё не забросил диссертацию.
Самые увлекательные приключения у меня были с преобразованием Фурье.
Преобразование Фурье, если совсем просто, это представление сигнала в виде набора периодических функций с разными частотами. Было у нас по оси x время, а по оси y - значение. Выполнили прямое преобразование Фурье - получили по оси x - частоту, по оси y - амплитуду периодической функции. Одно из названий результата - спектр. Выполнили обратное преобразование - получили исходный сигнал. Если к спектру применить какую-то трансформирующую функцию (фильтр), то после обратного преобразования сигнал изменится. Например, если обнулить высокие частоты - сигнал почистится от шумов. Довольно обыденная вещь, владеть которой должен любой инженер.
Но есть некоторые нюансы, которые я изложу ниже. Самое первое, что поджидает бодрую личинку инженера, сдавшую курс матана и, возможно, поигравшуюся со спектроанализатором, при попытке использовать готовое преобразование Фурье из любого из математических пакетов - спектр НЕ ТОТ.
Реализованное во всех математических пакетах быстрое дискретное преобразование Фурье (FFT) выдаёт спектр ощутимо отличающийся от привычного. В привычном случае у нас ось частот (x) идёт слева направо, мы рисуем ее пересечение с осью амплитуд частотных компонент в нуле и радуемся жизни. В результате быстрого дискретного преобразования осей частот по факту две. Одна идёт с начала массива, содержащего результат преобразования и занимает его первую половину. По ней идут положительные частоты. Вторая начинается в конце массива, и идет к середине, соответствует отрицательным частотам и занимает вторую половину массива с результатом преобразования. Наглядную иллюстрацию выложу в следующем посте.
Соответственно, накладывая фильтры на спектр, надо помнить об отрицательных частотах и воздействовать на них согласно задаче.
Для визуализации спектра достаточно выводить график первой половины массива со спектром - положительные частоты.
P.S. Раньше я касался преобразования фурье в далёком 2021 году, когда ещё не забросил диссертацию.
🔥3