Блог им. AlexanderTomtosov

Системно тестируем аномалии на Python. Релиз библиотеки Portfolio Quantitive Research (PQR)

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

  • Моделирование портфелей по кросс-секции и временным рядам;
  • Квантильная методика формирования портфелей в % от выборки или фиксированное число инструментов;
  • Возможность гибко задавать веса в портфеле по дополнительному фактору (почти smart beta);
  • Можно вырывать данные для аналитики на каждом промежуточном этапе: сделки, размер позиций, комиссии, доходность портфелей;
  • Возможность относительно точно учесть комиссионные расходы;
  • Пока самая простая визуализация и метрики.

Как выглядит итоговая отрисовка:
Системно тестируем аномалии на Python. Релиз библиотеки Portfolio Quantitive Research (PQR)

Небольшая предыстория или зачем писать свой тестер

 

Не являясь базовым программистом, я пользовался готовыми решениями для бэктестов и особенно долго засиживался на платформе Quantopian. В прошлом году компания не получила нового транша от инвесторов и объявила о закрытии. Вместе с ней сгинул и весь написанный код, а знания синтаксиса несуществующей платформы близки по полезности к 1С-программированию при переезде в долину.
Поработав с другими сервисами, понял, что их существенные недостатки можно разделить на 3 группы:

 

  1. Отсутствие гибкости в плане выбора параметров и получения промежуточных результатов;
  2. Работа в закрытой экосистеме из данных и тестов. Проблемно загрузить много своих альтернативных данных или существенно поменять логику тестов;
  3. Отсутствие системного тестирования рынка и работа по отдельным инструментам.

 

Последний пункт наиболее критичный. Мне хотелось тестировать технические индикаторы или отбор акций по EV/EBITDA не на выборке из 1/2/25 бумаг, а на всех 500+ акциях, которые обращались на Мосбирже или 10к+ на NYSE. Данных и идей будет много, поэтому программа должна быть умеренно быстрой. Остановился на векторных вычислениях в numpy.
Логика расчетов не убойная и ее не сложно переписать на ваш любимый язык. Пример c расчетом весов портфеля по заданному фактору. Например, это может быть рыночная капитализация:

Системно тестируем аномалии на Python. Релиз библиотеки Portfolio Quantitive Research (PQR)

Как можно тестировать свои идеи в PQR

  1. Загружаем библиотеку и смотрим примеры использования в main.py и в папке с примерами. Там ноутбуки с простыми идеями. Будут пополняться;
  2. Находим данные по ценам и факторам для множества компаний. Цена тоже может быть фактором, если ее преобразовать в изменение или другой показатель. Допустим, выбраны мультипликаторы P/E и месячные цены закрытия для 200 российских акций;
  3. Составляем одинаковые по размеру таблицы (матрицы) в экселе/csv/txt или в чем угодно, что сможете загрузить в программу. По вертикальной оси будут названия компаний, а по вертикальной временной ряд. Матрицы обязательно должны быть одинаковы по размеру;
  4. Выбираем параметры тестирования: сколько периодов мы наблюдаем за фактором перед покупкой и сколько будем держать позиции. Устанавливаем лаг, комиссии и требуемый % акций из выборки в каждом портфеле;
  5. Подаем данные по P/E в функцию get_factor и получаем готовые ряды для дальнейших тестов. Все расчеты по укрощению lookahead bias выполняются здесь. Дополнительно сдвигать ряды не нужно;
  6. Задаем фильтры (опционально). Например, выкидываем из выборки компании со среднедневным объемом торгов за прошлый месяц менее 100 млн рублей;
  7. Считаем позиции по отдельным бумагам в каждом портфеле. Эти бинарные матрицы уже почти портфели, но не хватает весов;
  8. Задаем способ расчета весов в портфеле. Если выбираете взвешенный по фактору, то этот фактор нужно загрузить, не забывая про размер матрицы;
  9. Опционально считаем комиссии;
  10. Подаем все полученные данные в последний скрипт для расчета доходности портфеля, метрик и сравнения с бенчмарком. Если есть интересная альфа – можно углубиться в отдельные портфели и подумать над усложнением стратегии. Если нет – копаем дальше.

В папке examples на Гитхабе есть ноутбук с примером расчета небольших портфелей из FAANG и анти-FAANG акций. Пример ознакомительный и по понятным причинам выкладывать большие массивы вендоров в открытый доступ не могу.

Почему квантильная методика так важна

Количество акций в отдельные периоды на ЕМ сильно отличается, особенно если используете фильтры по ликвидности. Количество бумаг на нашей бирже в 1997 и 2021 не подлежит сравнению. 10 портфелей по 5 ликвидных (относительно) акций это очень много для того периода и мало для текущего. Квантильный способ удобен тем, что мы всегда берем одинаковый относительный охват рынка и получаем полную картину на каждый период.

Интересно наблюдать, как меняется результативность портфеля от первых 10% с наименьшим P/E, затем следующим 10% и т.д. Если альфа ломается при малейших изменениях параметров – тревожный признак. U-образная доходность портфелей – уже интереснее.

Что будет и чего не будет в будущих версиях

  1. Расширю количество метрик и графиков, добавлю мультифакторные стратегии и способы расчета бенчмарка
  2. Возможно будет коллаборация с подгрузкой данных из бесплатных источников;
  3. Больше статистики и аналитики по карте портфелей;
  4. Точно не будет всего что касается исполнения ордеров, производных инструментов и микроструктуры рынка. Это инструмент для первых прогонок стратегий. Копать дальше и торговать лучше более подходящим инструментом.

Ссылку на гитхаб с программой выложил в телеге :) https://t.me/sentimetrica

В следующий раз разберу принцип работы на конкретных примерах по нашему рынку. Идеи и пожелания по инструменту @atomtosov

★22
19 комментариев
По России интересно. По Штатам все уже придумано в лучшем виде.
Для анализа можно использовать готовую довольно мощную либу pyfolio от quantopian, может работать изолированно без экосистемы quantopian
Так же можно использовать zipline локально
Есть еще альтернатива с quantconnect, тоже доступна для локальной работы

Понимаю, что свой велосипед приятнее и роднее )
Но все же рекомендую глянуть на готовые решения, если вдруг пока не довелось, возможно, они закроют большую часть потребностей
Михаил Дунаев, Спасибо, что напомнили про них. Может они просто не под мои нужды :) постараюсь добраться до сравнительного анализа тестеров. Все-таки кажется, что там про другое.
Александр Томтосов, отбор акций и формирование портфелей по фундаментальным показателям возможен на квантконекте, без танцев с бубном над подготовкой выборок и тд. 
Dancing Orange Hyena, отбор по фундаменталу? То есть можно с квантконнекта качать годовые и квартальные прибыли и цены с глубиной? А как насчет делистингованных компаний?
avatar

broker25, приветствую. Делистингованные компании тоже есть(свойство delisting) Вот ссылка на доку по ресерчу. www.quantconnect.com/docs/research/fundamental-data
Качать данные… наверно можно исхитрится, чтобы отправлять себе почту. Но по сути это ненужно.
Вот пример кода по отбору и сделкам на основе балансовой стоимости. Пример на питоне. На С# переключать не стал, так как сейчас идет бектест, чтобы ничего не сломалось. Но старожилы квантконекта рекомендуют использовать С#, так как бектесты работают в десятки раз быстрее. К сожалению отступы не копируются на смарт лабе. Но зная вас, думаю общее впечатление себе вы сможете составить.
_________________________________

from datetime import timedelta
from QuantConnect.Data.UniverseSelection import *
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel

class LiquidValueStocks(QCAlgorithm):

def Initialize(self):
self.SetStartDate(2017, 5, 15)
self.SetEndDate(2017, 7, 15)
self.SetCash(100000)
self.UniverseSettings.Resolution = Resolution.Hour
self.AddUniverseSelection(LiquidValueUniverseSelectionModel())

#1. Create and instance of the LongShortEYAlphaModel
self.AddAlpha(LongShortEYAlphaModel())

self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.SetExecution(ImmediateExecutionModel())

class LiquidValueUniverseSelectionModel(FundamentalUniverseSelectionModel):

def __init__(self):
super().__init__(True, None, None)
self.lastMonth = -1

def SelectCoarse(self, algorithm, coarse):
if self.lastMonth == algorithm.Time.month:
return Universe.Unchanged
self.lastMonth = algorithm.Time.month

sortedByDollarVolume = sorted([x for x in coarse if x.HasFundamentalData],
key=lambda x: x.DollarVolume, reverse=True)

return [x.Symbol for x in sortedByDollarVolume[:100]]

def SelectFine(self, algorithm, fine):
sortedByYields = sorted(fine, key=lambda f: f.ValuationRatios.EarningYield, reverse=True)
universe = sortedByYields[:10] + sortedByYields[-10:]
return [f.Symbol for f in universe]

# Define the LongShortAlphaModel class
class LongShortEYAlphaModel(AlphaModel):

def __init__(self):
self.lastMonth = -1

def Update(self, algorithm, data):
insights = []

#2. If else statement to emit signals once a month
if self.lastMonth == algorithm.Time.month:
return insights
self.lastMonth = algorithm.Time.month

#3. For loop to emit insights with insight directions
# based on whether earnings yield is greater or less than zero once a month
for security in algorithm.ActiveSecurities.Values:
direction = 1 if security.Fundamentals.ValuationRatios.EarningYield > 0 else -1
insights.append(Insight.Price(security.Symbol, timedelta(28), direction))
return insights

Dancing Orange Hyena, спасибо, интересно. Возможно, качну, проверю. На самом деле у меня не было особых проблем со скоростью Питона при тесте фундаментала. А вот с тиками он явно не справится, даже минутки под вопросом
avatar

broker25, а ничего качать не надо. Там же webIDE. При бектестах дается бесплатная виртуалка. Все деплоится нажатием кнопки. Раздел рисерч предназначен для быстрого поиска идей(я новенький в квантконекте, по этому не разобрался нафиг он нужен когда есть webIDE, аж с двумя подходами: класcическим и на основе нового фремворка).  Я пока пытаюсь разобраться с классическим подходом. Там вобще все просто относительно. Но я не профессиональный прогер, поэтому иногда возникают глупые затыки. Но если уж такой тупень как я, смог начать в этом потихоньку разбираться, то вы разберетесь очень быстро.

 

broker25, а еще там есть опционы
Михаил Дунаев, а в zipline правда лежат исторические цены акций с таймфреймом минута?
avatar
broker25, если говорить про локальное использование, то зависит от того, какие данные туда положишь, можно и дневные и минутные
а panda не изучвли?
avatar
 как вообще начали питон осваивать. мне 31 год и я вообще не шарю в программировании. хочу на досуге питон начать учить, но так лень.
avatar
Артур, в инете полно уроков, начните с малого и шаг за шагом…
avatar
Артур, рекомендую платформу stepic, там есть бесплатные курсы и для начинающих и для продвинутых
Это все прекрасно, но у вас цены с яху, а фундаментал вообще не качается. Будете добавлять из открытых источников?? А они такие есть в принципе? И это противоречит идее тестирования без ошибки выжившего
avatar
broker25, фундаментал нормально можно вытащить с яху.финанс, там есть апи для этого
В открытых источниках есть проблема с компаниями которые больше не торгуются, но есть недорогие платные, например, tiingo за 10$ /мес
Есть еще market-archive.appspot.com, не проверял лично, но на сайте указано, что за 60$ можно получить дневки по всем акциям, в том числе и снятым с листинга
Михаил Дунаев, то есть на сайте yahoo.finance все закрыто, кроме последних лет, а в апи они дают к данным доступ? Может быть, конечно, но предполагаю, что как на сайте
avatar
broker25, да, подзабыл я этот момент, на яху только недавние данные отдаются, как на сайте
Ковырял тут otcmarkets, на удивление, там можно вытащить историю не только по otc рынку, но и по основному
Подергал недокументированное апи на счет всяких эплов и фэйсбуков
Годовые отчеты есть до 96 года, квартальные только до 2015

теги блога Александр Томтосов

....все тэги



UPDONW
Новый дизайн