Блог им. raxat

Шаблон торговой системы на Python (backtrader, quantstats)

    • 22 сентября 2021, 21:54
    • |
    • Diamond
  • Еще
Сначала я пытался бэктестить системы в TradingView и этого было достаточно для быстрой оценки торговых гипотез, но оказалось, что мало просто знать, где купить и где продать. Не менее важно понимать, сколько купить или продать и для этого нужны другие инструменты.

Зачем Python?

Лично мне он показался удобнее. Например, можно быстро подключить telebot и система начнёт отправлять сигналы прямо в телегу на все девайсы. Работать со скриптами можно даже на айпаде где-нибудь в дороге, тоже плюс.

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

Что потребуется?

— backtrader для логики торговой системы

— quantstats для формирования отчёта

— Jupyter Notebook, если нужно удобнее редактировать код

— Сам Python и установщик пакетов pip

Сам код достаточно простой:

import datetime
import webbrowser
import backtrader as bt
import quantstats as qs
import yfinance as yf

class SmaCross(bt.SignalStrategy):
    params = dict(
    pfast=50, # период быстрой средней, 50 свечей
    pslow=200 # 200 свечей для медленной средней
    )

    def __init__(self):
        sma1, sma2 = bt.ind.SMA(period=self.p.pfast), bt.ind.SMA(period=self.p.pslow) # используем простые скользящие средние
        self.crossover = bt.ind.CrossOver(sma1, sma2)

    def next(self):
        if not self.position:  # если позиция не открыта системой
            if self.crossover > 0:  # если быстрая SMA пересекает медленную вверх
                self.buy()  # открываем длинную позицию

            elif self.crossover < 0:  # во всех остальных случаях закрываем открытую позицию
                self.close()

data = bt.feeds.PandasData(dataname=yf.download('MSFT', '2007-02-02', '2021-09-01', auto_adjust=True)) # в качестве примера берём MSFT, 2008 год тоже торгуем

cerebro = bt.Cerebro()
cerebro.broker.setcash(10000) # начальный депозит 10 000 денег
cerebro.addsizer(bt.sizers.FixedSize,stake = 100) # размер позиции 100 лотов
cerebro.adddata(data)
cerebro.addstrategy(SmaCross)
cerebro.addanalyzer(bt.analyzers.PyFolio, _name='PyFolio') # PyFolio, но quantstats его тоже отрендерит
results = cerebro.run()

strat = results[0]
portfolio_stats = strat.analyzers.getbyname('PyFolio')
returns, positions, transactions, gross_lev = portfolio_stats.get_pf_items()
returns.index = returns.index.tz_convert(None)

qs.reports.html(returns, 'MSFT', output='stats.html', title='SMA Cross') # формируем репорт в quantstats, вместо бенчмарка SPY используем buy&hold MSFT
webbrowser.open('stats.html') # рендерим репорт в html и открываем в новой вкладке


В результате quantstats должен сгенерировать примерно такой отчёт:
Шаблон торговой системы на Python (backtrader, quantstats)

MSFT это трендовый инструмент и система на основе скользящих средних будет иметь положительное матожидание, однако она существенно проиграет Buy & Hold, поскольку средние отстают от рынка. Такая система также 83% времени находится в рынке, но при этом её максимальная просадка составляет 19.45% и это заметно ниже, чем buy & hold, который даёт просадку до 57.94%. Возможно, система могла бы использовать кредитное плечо для повышения доходности.

Диаграммы финансовых результатов явно указывают на необходимость сокращения убытков:
Шаблон торговой системы на Python (backtrader, quantstats)
Данные берутся с 2007 года, но система начинает торговать только в 2009:
Шаблон торговой системы на Python (backtrader, quantstats)

Теперь у нас есть аналог «Hello, world!», только для алготрейдинга и в нём уже есть что улучшить. Можно ещё отрендерить свечной график backtrader со сделками, но Jupyter это не поддерживает:

cerebro.plot(style='candlestick')



★45
29 комментариев
Перед тратой времени и денег на алготрейдинг советую математически доказать себе и своей семье способность алгоритма, созданного на основе обработки известных данных в прошлом, генерировать устойчивую прибыль на неизвестных данных в будущем.
avatar
$100, все нормас, я не просто так в это полез)
avatar
$100, для доказательства достаточно одного примера.
этот пример продавай в мае и уходи.
он работает).
конечно не 100% в год.
но альфа есть до сих пор.
и есть причина почему он продолжит работать.

avatar
Антон Б, крутая кстати неэффективность. В этом году тоже сработала, но я её проигнорил и оставил бумаги с высокой бетой в портфеле — нормальный такой бумажный убыток поймал)
avatar
Diamond, да. работает все время.
и есть экономическая причина почему продолжит в будущем.
внешняя относительно рынков.

хотя конечно 100% в год не дает )
хорошо затыкает рот тем кто говорит что алго не работает в принципе.
потому что будущее не определено.

avatar
Антон Б, это распространённая лажа, а не «причина». Причём она даже именно в тех самых Штатах статистически давно опровергнута как на узком, так и максимально широком наборе бумаг и полувековой стате:

Сентябрь вон под «гоу эвэй» куда больше подходил и подходит, однако о нём таких баек выдумывать уже не стали.
avatar
Дмитрий, смотри твой график.
продавая 1 мая и покупая 1 октября вы прямо в 21 году выкинули 2 убыточных месяца.
и оставили только прибыльные.

и при этом говорите что не работает.

как так?

цель всех алгоримов отдать риск и взять ревард.
вот стратегия отдает риск.

avatar
Антон Б, у вас глаза есть? Ведь даже в этом году — видно что по обоим таблицам плюс перекрыл минус. А уж в условные «средние» года — тем более.
Это даже не оспаривая ваше 1 октября, ибо это лишь ваша натяжка постфактум (да ещё лишь этого конкретного одного года). Т.к. в оригинале сентябрь в перерыв отнюдь не включается.
avatar
Дмитрий, плюс перекрыл минус… наверно.
но цель отдать риск и взять ревард.
а не бороться с минусом и принимать риски.

даже тот же етф на sp500 имеет просадку историческую 51%
что очень много.
по этому его приходится разбавлять облигациями чуть не на 50%.
облигациями которые в среднем НЕ ПОКРЫВАЮТ инфляцию.

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

+ вы 5 месяцев не в риске.
и цена этих денег тоже 0.
потому что вы можете собрать из них синтетику и покрыть инфляцию.
(почти)

ну и так далее.

это работающий пример.
который работает.
но 100% годовых не приносит.

свою функцию ПРИМЕРА — тыкать носом тем кто говорит что алго не работает потому что будущее не определено он выполняет.

avatar
Антон Б, «свою функцию ПРИМЕРА — тыкать носом тем кто говорит что алго не работает потому что будущее не определено он выполняет.»
В том и дело, что как раз НЕ выполняет. Совокупный плюс за оригинальные 4 мес(ибо в оригинале как раз май+лето) с лихвой  перекрывает историческую среднегодовую инфляцию в долларе(она чуть более 2% годовых) за этот же срок. Что показывает, что регулярный ежегодный 4х месячный выход из акций был нецелесообразен, несмотря на опис. вами волу. Взятую, как я понял, лишь от одного 2008го.
Вообще, перед тем как соберетесь таки кого-то высокомерно «тыкать носом», хотя бы сами разберитесь с вопросом, а то тыкать начнут уже вас 😉
Удачи!
avatar
Дмитрий, прикольно.
для того чтобы ткнуть носом нужен 1 Любой! пример.
(для доказательства существования нужно любой пример на выбор того кто доказывает существование)

я его привел.
это не такой пример как думал Дмитрий — значит он не подходит?
(«продавай в мае и уходит » я нигде не сказал когда приходить, вы придумали это за меня.)
Показывать что алго работает: берите и получайте доход.
Уменьшайте риски.
(а уменьшить риск важнее чем получить больше доход)
Вот вам базовый пример.
Вызывает у людей буквально ненависть!.

зато торговать руками как зомби!
буквально как зомби.

это то что нужно? ).
avatar
Антон Б, какая ненависть, вы о чем?
Даже отдельный май ПЛЮСОВОЙ — что прекрасно видно из обоих таблиц. Это притом, что за эти полвека в США было аж 3 мощнейших кризиса на фонде. Куда как тяжелее всяких короновирусов.
А про руки вообще не ко мне. Я исключительно фундаменталист, да ещё и долгосрочник, живущий от дохода с инвестпортфеля. Над сливами типичного трейдунья могу лишь недоумевать, а уж никак не самозабвенно «торговать руками».
avatar
Дмитрий, в оригинале, и в вашем графике сентябрь исключается.
просто вокруг прибыльных идей много лжи.
никому не интересно чтобы прибыльные и простые идеи попадали в общество.
avatar
У меня уйма торговых систем, сказочно прибыльных, если не учитывать комиссию и проскальзывание.
А если учитывать, то на длинной истории торгов возникают неприемлемо большие интервалы без прибыли. И от таких систем мне не нужны никакие сигналы ни на какие девайсы.
Для таких результатов вполне хватает программы WealthLab со скриптами на C#.
avatar
Rostislav Kudryashov, зато этих ботов можно потом продавать )
avatar
какие скользящие??!!! а просто смотреть, что и сколько покупают большие дядьки, и повторять??..
avatar
Сергей В., не всегда понятно, в какую сторону эти дядьки торгуют)
avatar
В шорт нужны средние с меньшим периодом и участием в сделке. В индюке Ишимоку есть простой сигнальщик  ref(C,+9) . Вообще Ишимоку готовая система. Там берется середина цены временного диапазона и она выдвигается из прошлого в настоящее(облако).У Билла В. на этом же принципе аллигаторы.
avatar
Зачем изобретать велосипед, если есть движок TSLab?
avatar
Michael, его тоже пробовал, но он платный и в него я въезжал дольше, чем в Python
avatar
Michael, там все не так просто.
в конце вы получаете алго который стоит вам примерно 100к в год лицензии.
чтобы просто работать.

один коннектор стоит примерно 50к в год.
к одному брокеру.

а брокеров как минимум 2.
потому что нужно два — для рискменеджмента.

плюс если вы торгуете долго у вас всегда несколько разных счетов.
иис, жены, брата, подруги друга.
а там 50к за счет! у одного брокера.


avatar
Антон Б, а от каких рисков страхует одновременная роботорговля с 2-х брокеров? Эти риски стоят всего сопутствующего геммора?
avatar
Антон Б, не путайте бекстетинг и разработку стратегии с ее автоматической проторговкой. Если вы разрабатываете логику на Python — то вам все равно нужно вкорячивать коннекторы к брокеру. Речь о том, что для разработки стратегии и тестировании алгоритмов типа «скользящих средних» необязательно лезть в кодинг — все это уже бесплатно доступно в TSLab + хорошая визуализация сделок на графике.

Коннекторы — отдельный вопрос. 


avatar
Michael, OSA там есть коннекторы в открытом коде.
и не только том.
C#
пишу )
а вот идеи тестирую на pine и python

потому что пишется тест быстрее в разы.
avatar
за кванстат спасибо ) 
avatar
В Backtrader не предусмотрена возможность гипероптимизации параметров, т.е. прогнать по всей области параметров каким-то интсрументом из машинного обучения простым способом не получится. Можно сделать оберткув над инициализацией, но это очень медленно. У меня получалось доя двух МА на обычном ПК где-то 50 циклов на небольшой истории в секунду. Но стратегии сложнее гораздо медленней — на 15 минутках за пару лет по 2-3 секунды на цикл. Если параметров в оптимизаци. От 5-10 по 10 значений в каждом — то BT просто не подойдет. А медленно из-за самой архитектуры — здесь ООП и классы и очень много действий на каждый шаг вне pandas. Соответствеено и задержки.
avatar
Спасибо за пост! Запили плиз пост про тестирование стратегий на TV и ограничения.
avatar
Ошибка при запуске:

[*********************100%***********************] 1 of 1 completed
Traceback (most recent call last):
File «C:\Users\cloud\PycharmProjects\MyTradeBot\MyBot.py», line 40, in <module>
qs.reports.html(returns, 'MSFT', output='stats.html', title='SMA Cross') # формируем репорт в quantstats, вместо бенчмарка SPY используем buy&hold MSFT
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File «C:\Users\cloud\PycharmProjects\MyTradeBot\venv\Lib\site-packages\quantstats\reports.py», line 87, in html
benchmark = _utils._prepare_benchmark(benchmark, returns.index, rf)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File «C:\Users\cloud\PycharmProjects\MyTradeBot\venv\Lib\site-packages\quantstats\utils.py», line 269, in _prepare_benchmark
benchmark = benchmark_prices.reindex(new_index, method='bfill') \
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File «C:\Users\cloud\PycharmProjects\MyTradeBot\venv\Lib\site-packages\pandas\core\series.py», line 5094, in reindex
return super().reindex(**kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File «C:\Users\cloud\PycharmProjects\MyTradeBot\venv\Lib\site-packages\pandas\core\generic.py», line 5289, in reindex
return self._reindex_axes(
^^^^^^^^^^^^^^^^^^^
File «C:\Users\cloud\PycharmProjects\MyTradeBot\venv\Lib\site-packages\pandas\core\generic.py», line 5304, in _reindex_axes
new_index, indexer = ax.reindex(
^^^^^^^^^^^
File «C:\Users\cloud\PycharmProjects\MyTradeBot\venv\Lib\site-packages\pandas\core\indexes\base.py», line 4412, in reindex
indexer = self.get_indexer(
^^^^^^^^^^^^^^^^^
File «C:\Users\cloud\PycharmProjects\MyTradeBot\venv\Lib\site-packages\pandas\core\indexes\base.py», line 3912, in get_indexer
return self._get_indexer_non_comparable(target, method=method, unique=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File «C:\Users\cloud\PycharmProjects\MyTradeBot\venv\Lib\site-packages\pandas\core\indexes\base.py», line 6182, in _get_indexer_non_comparable
raise TypeError(f«Cannot compare dtypes {self.dtype} and {other.dtype}»)
TypeError: Cannot compare dtypes datetime64[ns, America/New_York] and datetime64[ns]

Process finished with exit code 1

avatar

теги блога Diamond

....все тэги



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