Как я создавал систему для алгоритмического трейдинга на Rust и о чем сожалею

Если вы захотите узнать о языке программирования Rust в интернете, то гарантирую, что найдете только положительную информацию. Практически все, что вы прочитаете  —  будь то руководство на Medium, пост на Reddit или ответ на Stack Overflow,  —  будет написано в позитивном ключе.

Пост на GitHub о Rust как о любимом языке разработчиков

Справедливости ради стоит отметить: понять, почему так превозносят Rust, нетрудно. Он является одним из самых быстрых языков программирования. В отличие от своего конкурента C++, Rust по умолчанию безопасен для памяти и потоков. А значит, программистам придется постараться, чтобы написать небезопасный Rust-код.

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

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

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

Рефакторинг прошел с большим успехом и привел к созданию инвестиционной платформы нового поколения под названием NexusTrade.

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

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

Погружение в бассейн с цепными пилами

К слову сказать, другим претендентом на реорганизацию моей платформы был Golang. В отличие от Rust, который вызывает множество мнений, имеет сложный синтаксис и слишком многословен, Golang  —  это быстрый и простой в использовании язык, созданный компанией Google. Как и Rust, он обладает многопоточностью и молниеносной скоростью. Однако в Golang есть сборщик мусора, что делает его чуть менее производительным, чем Rust.

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

Самое главное преимущество Rust  —  полное устранение целого класса ошибок  —  является и его самым большим недостатком. Система статического контроля ссылок чрезмерно усложняет Rust, особенно по сравнению с такими языками, как TypeScript и Go.

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

Приступив к изучению Rust, я нырнул головой вперед в бассейн с цепными пилами на дне. Да еще к тому же я не умел плавать.

Пришлось быстро учиться.

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

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

Из цепей Rust к идеальному коду: как ChatGPT стал моим спасательным кругом

Если честно, то без ChatGPT я, скорее всего, начал бы все с нуля и перешел на Go. Rust оказался для меня крепким орешком. Добиться значимого прогресса на платформе, одновременно изучая язык и работая на полную ставку, было бы просто невозможно. Но OpenAI выручил меня!

Я точно знал, какой должна быть логика: однажды я уже написал ее на TypeScript! Сложность заключалась в том, чтобы заставить ее работать на языке Rust. Нужно было решить как мелкие задачи (типа корректной работы с MongoDB и сериализации/десериализации данных), так и более серьезные проблемы (например, представление объектно-ориентированной структуры TypeScript-кода в модели владения и заимствования Rust). ChatGPT  —  с его пониманием синтаксических нюансов и эффективной, но сложной системы типов Rust  —  провел меня через все подводные камни. У меня было ощущение, что рядом со мной опытный Rust-разработчик, готовый объяснить тонкости времен существования и элегантность выражений сопоставления.

Наконец, я дошел до того момента, когда мне уже не нужен был ChatGPT для каждого вызова функции и новой структуры. Мне стало комфортно работать с языком, и я начал ценить элегантность дизайна. И все это при сохранении продуктивности! Таким образом, началось создание совершенно новой платформы, для которой чрезвычайное значение имела производительность. 

Плавание по синтаксису: плотные воды Rust и туманные моря MongoDB

Разрабатывая свою платформу, я заметил, что написание Rust похоже на чтение книги, которую все называют шедевром, но текст в ней настолько плотный, что вы не уверены, наслаждаетесь ли сюжетной линией или просто гордитесь собой уже за то, что до вас дошел смысл предложения. Конечно, в Rust есть свои плюсы, но и сложностей немало. Мне казалось, что всякий раз, когда я пытался сделать что-то простое, Rust требовал от меня написать эссе о том, почему я этого достоин. До сих пор не до конца понимаю макросы, а ведь они, пожалуй, являются важнейшей частью языка!

А еще есть MongoDB, к которой я оказался совершенно не готов. TypeScript ведет вас за руку. У вас есть удобная ORM вроде Mongoose, и все работает чудесно и легко “из коробки”. Есть такие помощники, как populate, и я даже не подозревал, что они выполняют логику “под капотом”. По наивности я думал, что MongoDB  —  это встроенная функция. Но я ошибался.

В Rust выполнять такие простые вещи, как findById и save, было в 1000 раз сложнее, чем в TypeScript… казалось бы, без всякой причины. Некоторые концепции, такие как транзакции, в Rust, казалось, даже не существовали. А если говорить об ошибках сериализации/десериализации… Сообщения об ошибках были, безусловно, худшими из всех, которые я когда-либо видел.

error getting backtest: Kind: expecting DateTime, labels: {} (ошибка при получении бэктеста: Вид: ожидание DateTime, метки: {})

Ожидание DateTime для чего? Какое поле? Какая структура? Почему эти сообщения об ошибках так неудачно составлены? Как выяснить, какое поле является проблемным?

Полагаю, копаясь в базе данных и непосредственно сравнивая код.

Что мне понравилось в Rust

До сих пор я рассказывал о том, что мне не нравится в Rust. На самом деле, я не ненавижу этот язык.

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

Тем не менее есть вещи, которые мне действительно нравятся в Rust. Например, если код компилируется, вы можете быть почти уверены, что он работает так, как ожидалось. Вы знаете, что получаете максимально быструю реализацию, используете минимальное количество ресурсов и экономите деньги (и пространство среды) при развертывании.

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

В целом, я бы оценил язык на 8,4 (по 10-бальной шкале). Мне кажется, что он мог бы быть проще с фичей MongoDB ORM и менее многословным синтаксисом. Но если бы этот язык был идеальным, зачем нужны были бы другие языки?

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Austin Starks: I Built an Algorithmic Trading System in Rust. Here’s What I Regret

Предыдущая статьяVIM — это не только скорость
Следующая статьяУскоренный запуск системы “Аутентификации + база данных” (React.js и Firebase)