Skip to content
This repository was archived by the owner on Dec 28, 2023. It is now read-only.

Latest commit

 

History

History
88 lines (45 loc) · 20.6 KB

readme.ru.md

File metadata and controls

88 lines (45 loc) · 20.6 KB

С++17 фреймворк для алготрейдинга

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

Статус кода

Фреймворк находится на весьма раннем этапе разработки и не является законченным, возможны и баги и существенные изменения в будущем. Неустоявшийся код может быть в стиле "quick&dirty".

Условия использования

Код поставляется на условиях стандартной лицензии свободного ПО GNU GPLv3. Если вы хотите использовать части фреймворка так, как лицензия прямо не разрешает, пишите на [email protected], договоримся.

Если вы зарабатываете с участием t18 деньги, то, наверняка, должны понимать сколько труда стоит создать такую работу. Сделайте донат для покрытия расходов на разработку на своё усмотрение. За деталями так же пишите на [email protected].

Поддерживаемый компилятор

Т18 разработан с использованием превосходного компилятора Clang v6 набора LLVM, поэтому версия 6 или более новые версии Сlang будут работать сразу. Другие современные компиляторы, полноценно поддерживающие спецификацию С++17 теоретически должны работать тоже, но, возможно, придётся внести небольшие правки.

Среда Visual Studio 2015 (проекты которой присутствуют в исходных кодах) используется только как редактор кода и инструмент управления компиляцией тестов. Использование её не обязательно. Для компилятора VC версии 2015 фреймворк будет не по зубам, но, возможно, более свежие VC2017 или VC2019 уже справятся.

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

Внешние зависимости

Фреймворк кроме стандартных компонентов STL (использовалась версия stl из VC2015, но с другими не более старыми версиями проблем быть не должно) использует только некоторые компоненты библиотеки Boost в режиме "только заголовочные файлы" (использовалась версия 1.70, более свежие должны работать из коробки). Компиляции Boost не требуется.

Пользователю фреймворка настоятельно рекомендуется быть знакомым с boost::hana. Если вы не знакомы с этой крутейшей и удобнейшей библиотекой метапрограммирования, то независимо от использования t18 очень рекомендую вложить время в её изучение. Это совершенно потрясающая штука, которая позволяет в С++ не теряя статической типизации и эффективности кода со сравнительной лёгкостью ходить по граблям использовать возможности, ранее доступные лишь языкам с динамической типизацией, таким как Python или Matlab.

Для сборки тестов ещё потребуется собрать библиотеку Google Test. Использовалась версия 1.7, прогноза работоспобности более новых версий у меня нет.

Состав, особенности и основные возможности фреймворка

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

Везде далее при упоминании названия класса/шаблона подразумевается префикс неймспейса ::t18::

  • дата/время эффективно хранятся с точностью до микросекунд в одной 64-битной переменной в виде упорядоченных битовых полей, см. класс mxTimestamp.

    • Так же есть отдельные классы mxDate + milDate для дат и mxTime + milTime для времени с точностью до секунд. В основе эти классов используется представление даты/времени в виде единого числа. Префикc mx отличает классы, хранящие данные в машино-удобном формате (поля выравнены на битовую границу), от mil классов, где поля выравнены на десятичную границу (10 сентября 2015 года представляется числом 20150910). mil классы удобнее для человеко-читаемого задания или печати, а с mx быстрее работать процессору.
    • Для mxTimestamp реализованы некоторые элементарные операции вроде инкремента, разности (до года) и некоторых других с поддержкой високосных лет.
  • исторические данные хранятся в памяти в векторном, а не в структурном виде. Т.е., например, стандартные потоки котировочных данных open, high, low, close и т.д. хранятся в памяти как отдельные самостоятельные массивы, вместо того, чтобы собирать их в одну структуру и хранить массив таких структур.

    • Это решение позволяет при необходимости строить более эффективные алгоритмы за счёт векторизации кода, а так же облегчает передачу конкретных потоков данных в качестве параметров.
  • в качестве основного контейнера для хранения массивов выбран круговой буфер в реализации ::boost::circular_buffer<>. Это позволяет иметь бесконечно обновляемую историю данных не больше наперёд заданной длины (хотя затрудняет векторизацию некоторых алгоритмов, поскольку требует учёта того факта, что на самом низком уровне поток данных может оказаться разорванным на две части).

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

  • реализованы следующие алгоритмы скользящего усреднения: MA, EMA, DEMA, TEMA, и скользящих статистик: min, max, процентильный ранг, процентиль, элементиль (как процентиль, но без линейной интерполяции при непопадании), и процентиль с таймфрейма ниже (напр, "какой была медиана high минутных баров за последние 3 часа?").

    • Реализация алгоритмов преобразования таймсерий может выглядеть страшной из-за обилия наследований и шаблонов, но на самом деле она весьма проста и логична, не имеет run-time стоимости, и позволяет при необходимости заменять один алгоритм на другой не задумываясь ни о каких внутренних зависимостях реализации и прочей специфике.
  • конвертеры таймфреймов (они же "способы агрегирования ценовых данных") поддерживают работу как на ohlc данных, так и на тиковых (они же "обезличенные сделки" в Quik). Реализован "пустой" конвертер tfConverter::tfConvBase<> (полезен для бэктестинга) и конвертер в любой минутный таймфрейм до часа или суток tfConverter::dailyhm<>.

  • таймфреймы (агрегированный набор ценовых данных, объединённый одной логикой агрегирования) timeseries::Timeframe<> одного инструмента объединяются в тикеры tickerServer<>. Набор тикеров управляется шаблоном MarketDataStorServ<>. Соответственно, торговая стратегия может работать с любых количеством тикеров и таймфреймов в них.

    • Таймфреймы предоставляют возможность задания функций обратного вызова для событий начала/открытия и конца/закрытия бара. Тикеры так же имеют такую возможность (колбэки срабатывают с гранулярностью входящего ценового потока), но важно понимать, что колбэки в тикерах работают всегда ПЕРЕД колбэками таймфреймов, поэтому для того, чтобы при срабатывании колбэка, объекты таймфреймов имели бы актуальные данные, колбэк нужно цеплять именно к какому-то конкретному таймфрейму, а не к тикеру. В целом, колбэки для тикеров нужны скорее для технических необходимостей бэктестера. В торговых стратегиях (почти?) всегда нужны только колбэки от таймфреймов.
    • Описание шаблона таймфрейма почти готово к работе с любыми данными, но эта возможность пока не проверена и, возможно, даже не дописана, поэтому "прямо из коробки" рекомендую ожидать поддержку только обычных ohlc данных (тип описан структурой tsohlcv)
    • Всю чёрную магию по превращению описания скалярной структуры данных (такой, как tsohlcv) в хранилище набора массивов данных, осуществляет шаблон timeseries::TsStor<>, от которого в конечном итоге наследуется шаблон таймфрейма.
  • спекулятивный набор сделок "купил-продал" объединён в одну сущность - трейд. Объект трейда создаётся фреймворком и возвращается при успешном создании запроса на открытие позиции лонг или шорт. Для созданного трейда можно задать функцию обратного вызова, которая сработает при автоматическом завершении сделки (срабатывании стопа/тейка), или при сбое исполнения сделки (для бэктеста не актуально, актуально для живой торговли и с этим пока не закончено).

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

  • бэктестер описан шаблоном exec::backtester<> и поддерживает как ордера "по рынку", так и стоп-лимитные ордера (напр, купить по маркету, если цена выросла выше указанной). Стоп-лоссы и тейкпрофиты поддерживаются как с указанием абсолютной цены, так и в % от текущей.

    • данные в бэктестер можно подавать из файлов через feeder::singleFile<> при работе с одним тикером или feeder::multiFile<> при работе с несколькими тикерами. Конкретный формат файлов задаётся адаптером, параметризующим шаблон фидера. По умолчанию настроено на .csv файлы с Финама "date,time,o,h,l,c,v". Некоторые другие адаптеры описаны в неймспейсе feeder::adapters.

    • результаты бэктестера можно экспортировать в .csv в формате, аналогичным формату бэктестера Amibroker.

  • реализованы два примера торговых стратегий по пересечению двух скользящих средних.

    • Реализация ts::MaCross<> входит в лонг с 1% стопом и 3% тейком по пересечению быстрой и медленной MA по дневному таймфрейму за 30 минут до завершения торгового дня/сессии (в числе прочего, реализация показывает работу с таймфреймами и способ хранения и обработки массивов, содержащих рассчитываемые данные).

    • ts::MaCrossO<> представляет собой модификацию предыдущей стратегии, которая работает по открытию первого бара нового дня (реализация показывает вариант наследования описаний и кода торговых стратегий).

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

Кто оказался впереди, - присылайте пуллы.

Известные проблемы и косяки

  • из-за того, что активно используемый ассоциативный гетерогенный контейнер ::boost::hana::map не даёт гарантий на порядок "разворачивания" его элементов при выполнении над ними операций, то там, где фреймфорк явно или неявно обещает определённую последовательность действий (например, вызов обработчиков событий должен быть в порядке их установки), то, теоретически, на некотором неудачном сочетании компилятора и версии библиотеки, эта (последовательность) может оказаться нарушенной. Ошибка была обнаружена на уже позднем этапе разработки, когда всё менять было слишком дорого и главное не понятно, нужно ли. Дело в том, на момент разработки сама hana ещё находилась в активном развитии и hana::map полностью базировалась на другом гетерогенном контейнере hana::tuple, в котором эта гарантия последовательности "разворачивания" предоставлялась. Т.е. на конкретно использованном сочетании компилятора и библиотеки эта проблема реализоваться не должна никогда. Практика полагаться на неописанные внутренности чужого кода порочна, но по сумме всех обстоятельств, было принято решение ничего не трогать, пока проблема реально себя не проявит. Кроме прочего, юнит-тесты имеют некоторый шанс отловить её появление (это чтобы подсластить пилюлю, бхх).