Пробовали ли вы запускать код, который мы видели ранее? (поставьте отметку, если не понимаете, о чем я: Бэктестируйте свои торговые системы с помощью Python — разработка стратегий)

Если да, вы должны были заметить, что ничего не происходит, кроме странных вещей. Это потому, что нам нужно немного изменить наш код, а также добавить анализаторы в наш Cerebro. Но по одному, давайте сначала немного изменим код.

Но перед этим вы можете взглянуть на репозиторий, который я сделал для этой серии. Это на GitHub: Backtrader Series. Вам должно быть удобнее, если вы хотите проверить код, пока читаете эту статью.

Лучшее ведение журнала

При тестировании на истории важно иметь хорошее ведение журнала. Как вы видели ранее, плохое ведение журнала бесполезно. Логи у нас сейчас примерно такие:

2022-08-22 - 2022-08-22 - 21531.794921875  @ 22163.3245636679
2022-08-23 -  - 1  @ None
2022-08-23 -  - 1  @ None
2022-08-23 - 2022-08-23 - 21600.099609375  @ 22060.92002652373
2022-08-24 -  - 1  @ None
2022-08-24 -  - 1  @ None
2022-08-24 - 2022-08-24 - 21694.58984375  @ 21994.31453874669

Вы что-то понимаете? Нет! Я сделал это специально, чтобы доказать вам, что этим важно не пренебрегать.

Во-первых, давайте добавим что-то, что будет видно при создании ордера на покупку.

self.log('BUY CREATE, %.2f' % self.datas[0].close[0])
# Add this ^
orders = [self.buy()]
self.orders_ref = [order.ref for order in orders if order]

Затем мы изменим наш метод notify_order, чтобы лучше обрабатывать заказы:

def notify_order(self, order):
    if order.status in [order.Submitted, order.Accepted]:
        # Buy/Sell order submitted/accepted to/by broker - Nothing to do
        return

        # Check if an order has been completed
    if order.status in [order.Completed]:
        if order.isbuy():
            self.log('BUY EXECUTED, %.2f' % order.executed.price)
        elif order.issell():
            self.log('SELL EXECUTED, %.2f' % order.executed.price)

        # Not enough cash: order rejected
    elif order.status in [order.Canceled, order.Margin, order.Rejected]:
        self.log('Order Canceled/Margin/Rejected')
        
        # We remove order if it's useless    
    if not order.alive() and order.ref in self.orders_ref:
        self.orders_ref.remove(order.ref)

Мы также можем удалить notify_trade, потому что он нам пока не нужен.

Наконец, мы можем изменить наш self.log в методе next.

self.log('Close: {}, EMA: {}'.format(self.datas[0].close[0], self.ema[0]))

Дополнительные параметры бэктеста

В нашем Церебро отсутствуют некоторые параметры.

  • Наличные: каковы наши первоначальные деньги?
  • Sizer: сколько активов мы должны купить?

Для установки наличных мы используем cerebro.broker.setcash(cash) . Например: cerebro.broker.setcash(100_000) .

Затем мы должны добавить размер. Сайзеров много (все можно найти здесь). Мы начнем с фиксированного размера, то есть мы будем покупать одинаковый размер для каждого заказа.

cerebro.addsizer(bt.sizers.FixedSize, stake=1)

stake — это количество активов, которые мы покупаем.

Теперь давайте снова запустим наш код.

еще одно условие

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

Чтобы узнать, находимся ли мы на рынке, мы используем условие self.position в нашей стратегии. self.position равно › 0, если у нас длинная позиция, и ‹ 0, если у нас короткая.

Итак, мы можем изменить наш метод next:

def next(self):
    self.log('Close: {}, EMA: {}'.format(self.datas[0].close[0], self.ema[0]))

    if not self.position:

        if self.datas[0].close[0] < self.ema[0]:
            self.log('BUY CREATE, %.2f' % self.datas[0].close[0])
            orders = [self.buy()]
            self.orders_ref = [order.ref for order in orders if order]

    if self.datas[0].close[0] > self.ema[0]:
        self.close()

Теперь запустите код, и все должно быть хорошо. Мы видим операции BUY и SELL.

Еще некоторые улучшения ведения журнала

Теперь мы снова добавим notify_trade для отображения прибыли при закрытии сделки.

def notify_trade(self, trade):
    if trade.isclosed:
        self.log('PROFIT, %.2f' % (trade.pnl))

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

Мы будем использовать пакет termcolor.

pip install termcolor

Затем мы можем, например, изменить наш метод notify_trade, чтобы наша прибыль окрашивалась в зеленый цвет, если она положительная, или в красный, если она отрицательная.

from termcolor import colored
...
def notify_trade(self, trade):
    if trade.isclosed:
        self.log(colored('PROFIT, %.2f' % (trade.pnl), 'green') if trade.pnl > 0
                 else colored('LOSS, %.2f' % (trade.pnl), 'red'))

colored просто принимает строку в качестве первого параметра и название цвета в качестве второго параметра.

Анализаторы

В бэктрейдере Анализатор — это такой же объект, как Сайзер или Стратегия. Это просто то, что вы добавляете в Cerebro.

В backtrader много анализаторов. Мы увидим только один, остальные вы можете найти здесь.

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

Сначала добавляем его в наш Cerebro:

cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trade')

Мы предоставляем необязательный параметр _name, чтобы дать имя нашему анализатору.

Затем мы должны сохранить результат cerebro.run :

result = cerebro.run()

Теперь мы можем получить доступ к анализу нашего анализатора:

strategy_result = result[0]
trade_analyzer = strategy_result.analyzers.trade
analysis = trade_analyzer.get_analysis()
print(analysis)
  1. Мы запускаем только одну стратегию, поэтому результат нашей стратегии сохраняется в result[0].
  2. Мы получаем наш анализатор с именем trade .
  3. Мы вызываем get_analysis(), чтобы получить анализ от нашего анализатора. У каждого анализатора есть метод get_analysis.
  4. Распечатываем анализ.

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

pnl_net_total = analysis.pnl.net.total
pnl_gross_total = analysis.pnl.gross.total
commissions = pnl_gross_total - pnl_net_total

winners = analysis.won.total
losers = analysis.lost.total
win_rate = winners / (winners + losers) * 100

print("---ANALYSIS---")
print("PnL net total: {}".format(pnl_net_total))
print("PnL gross total: {}".format(pnl_gross_total))
print("Commissions: {}".format(commissions))
print("Win rate: {}".format(win_rate)

Корректировки

Если вы запустите код, вы кое-что заметите. Комиссии равны 0. Таким образом, наш бэктест неверен, потому что, когда вы торгуете по-настоящему, есть комиссии. Давайте исправим это. Мы также добавим немного проскальзывания.

cerebro.broker.setcommission(commission=0.001) # 0.1%
cerebro.broker.set_slippage_perc(0.001) # 0.1%

Теперь мы можем запустить наш код:

---ANALYSIS---
PnL net total: -13241.988396214756
PnL gross total: -11074.33021484366
Commissions: 2167.6581813710964
Win rate: 68.96551724137932

У нас есть комиссии, ура! (мы никогда не были так счастливы получать заказы...) Это означает, что теперь наша модель приблизилась к реальности.

Визуальное представление

Мы можем захотеть иметь визуальный аспект нашей стратегии. Мы можем сделать это легко с помощью backtrader. Во-первых, давайте установим matplotlib.

pip install matplotlib

Теперь мы можем просто построить нашу стратегию:

cerebro.plot()

Если у вас возникла ошибка при попытке построить график, просто удалите предупреждения в импорте locator.py в папке matplotlib. Это известная проблема, связанная с тем, что backtrader не поддерживает последние версии matplotlib.

На графике вы видите, когда исполнялись ордера на покупку и продажу. Вы также видите экспоненциальную скользящую среднюю. Вы видите информацию о сделках и о брокере.

Последнее примечание

Не забудьте проверить Medium-backtraderSeries на моем GitHub, если вам нужен полный код для каждой истории из этой серии.

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

Правка: прочтите другие истории из этой серии здесь: Улучшите свою торговлю с помощью Python

Чтобы узнать больше о моих рассказах о Python, нажмите здесь!

Если вам понравилась история, не забудьте похлопать и, возможно, подпишитесь на меня, если хотите узнать больше о моем содержании :)

Вы также можете подписаться на меня по электронной почте, чтобы получать уведомления каждый раз, когда я публикую новую историю, просто нажмите здесь!

Если вы еще не подписаны на среду и хотите поддержать меня или получить доступ ко всем моим историям, вы можете использовать мою ссылку:



Сообщение от InsiderFinance

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь: