Поддержание безрискового портфеля с европейскими опционами

Финансовые производные

Производные финансовые инструменты дают возможность спекулировать и хеджировать базовый актив. Опционы - один из тех инструментов, которые обычно используются для хеджирования рисков портфеля. Существуют различные типы опционов: европейские: исполнение по истечении срока действия, американские: исполнение в течение всего срока действия опциона, бермудские: исполнение в установленные даты в течение срока действия опциона. вариант; в этом случае мы будем анализировать европейские варианты. Эффективность хеджирования определяется допущениями модели и методом, используемым для определения цены производного инструмента, а также частотой ребалансировки портфеля для поддержания хеджирования. Если бы нужно было использовать одношаговое биномиальное дерево для расчета дельты для опционного контракта, создать портфель на основе этой дельты и обновлять портфель с учетом этой дельты каждый месяц, чтобы поддерживать безрисковый портфель (хеджированный портфель) результат будет сильно отличаться от результата человека, который использует модель Блэка-Шоулза для расчета дельты каждый тик и обновляет портфель с учетом дельты на каждом тике, чтобы поддерживать риск бесплатное портфолио. Именно здесь вступает в игру алгоритмическое хеджирование: используя аналогичные принципы алгоритмической торговли, мы можем создать и поддерживать эффективный безрисковый портфель до тех пор, пока у нас не будет права реализовать свой опцион. Эта статья предназначена для использования в качестве своего рода конспекта, поскольку будет признано, что на практике транзакционные издержки препятствуют возможности непрерывного перехеджирования. Весь код для этой статьи можно найти на моем гитхабе здесь. Эта статья будет разбита на следующие разделы ...

  • Геометрическое броуновское движение - моделирование базового актива.
  • Биномиальные деревья - введение в ценообразование деривативов
  • Цены на опционы Блэка-Шоулза - отраслевой стандарт ценообразования опционов.
  • Алгоритмическое хеджирование - эффективная торговля безрисковым портфелем.

Геометрическое броуновское движение

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

  • S - «Курс акций сегодня»
  • Delta-S - «Изменение цены акций»
  • Му - «Срок дрейфа»
  • Дельта-Т - «Изменение во времени»
  • Сигма - «Волатильность»
  • Delta-Z - «Броуновское движение»

Геометрическое броуновское движение можно визуализировать относительно просто. Экспоненциальная линия тренда представляет термин дрейфа, а стрелки, показывающие отклонения от тренда, представляют броуновское движение. Стохастические отклонения от линии тренда (броуновское движение) пытаются зафиксировать изменения в информации для моделируемого актива. Член смещения можно предсказать, поскольку исторические цены на активы помогают вычислить значение для mu, но член броуновского движения не может быть предсказан, поскольку информация, которую он захватывает (хорошая, плохая, нейтральная), обычно случайна. К счастью, этот член можно описать нормальным распределением со средним нулевым и дисперсией Delta-T.

Геометрическое броуновское движение помогает нам объяснить поведение базового актива, фиксируя рост актива с течением времени, а также моделируя его стохастические характеристики. Теперь, когда мы знаем, как моделировать базовый актив, мы можем приступить к моделированию производных финансовых инструментов, которые они представляют.

Биномиальные деревья

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

Теперь, когда портфель определен, мы оцениваем его с помощью одношагового биномиального дерева.

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

Delta сообщает нам количество акций, необходимое для создания безрискового портфеля.

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

Цены на европейские опционы Блэка-Шоулза

Точность ценообразования биномиального дерева зависит от количества дискретных временных шагов. По мере увеличения числа дискретных временных шагов изменение во времени (Δt) приближается к нулю, и цена биномиального дерева сходится к цене, заданной моделью Блэка-Шоулза.

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

  • C - «Стоимость опциона колл в евро»
  • P - «Стоимость пут-опциона в евро»
  • X - «Начальная цена опциона»
  • S - «Курс акций сегодня»
  • T - «Время до истечения срока»
  • Сигма - «Волатильность»
  • ln - «Натуральный логарифм»
  • r - «Безрисковая процентная ставка»
  • N - «Кумулятивное нормальное распределение»

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

Частная производная стоимости опциона по отношению к цене базовых активов говорит нам о постепенном изменении в непрерывном времени для создания безрискового портфеля. К счастью, у нас есть интуитивное представление о том, что такое дельта в дискретном времени, что упрощает интерпретацию в непрерывном времени.

Алгоритмическое дельта-хеджирование

Теперь, когда мы знаем, как моделировать базовый актив, понимать дельту стоимости и оценивать опцион с помощью модели Блэка-Шоулза, мы можем реализовать эти уравнения на Python, чтобы помочь с проблемами управления портфелем в режиме реального времени. Мы можем начать с создания классов для европейских коллов и пут…

import math
import datetime
from datetime import timedelta
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy import stats
class EuropeanCall:
def d1(self, asset_price, strike_price, risk_free_rate, volatility, dt):
        return (math.log((asset_price/strike_price)) + (risk_free_rate + math.pow(volatility,2)/2)*dt)/(volatility*math.sqrt(dt))
def d2(self, d1, volatility, dt):
        return d1 - (volatility*math.sqrt(dt))
def price(self, asset_price, d1, strike_price, d2, risk_free_rate, dt):
        # Calculate NormalCDF for d1 & d2
        n1 = stats.norm.cdf(d1)
        n2 = stats.norm.cdf(d2)
        # Calculate call option price
        return asset_price*n1 - strike_price*(math.exp(-(risk_free_rate*dt)))*n2
def delta(self, d1):
        return stats.norm.cdf(d1)
def exercise_prob(self):
        return 1 - stats.norm.cdf(((self.strike_price - self.asset_price) - (self.drift*self.asset_price*self.dt))/((self.volatility*self.asset_price)*(self.dt**.5)))
def __init__(self, asset_price, strike_price, volatility, expiration_date, risk_free_rate, drift):
        self.asset_price = asset_price
        self.strike_price = strike_price
        self.volatility = volatility
        self.expiration_date = expiration_date
        self.risk_free_rate = risk_free_rate
        self.drift = drift
        # Calculate delta t
        dt = np.busday_count(datetime.date.today(), expiration_date) / 252
        # Calculate d1
        d1 = self.d1(asset_price, strike_price, risk_free_rate, volatility, dt)
        # Calculate d2
        d2 = self.d2(d1, volatility, dt)
        self.dt = dt
        self.price = self.price(asset_price, d1, strike_price, d2, risk_free_rate, dt)
        self.delta = self.delta(d1)
class EuropeanPut:
def d1(self, asset_price, strike_price, risk_free_rate, volatility, dt):
        return (math.log((asset_price/strike_price)) + (risk_free_rate + math.pow(volatility,2)/2)*dt)/(volatility*math.sqrt(dt))
def d2(self, d1, volatility, dt):
        return d1 - (volatility*math.sqrt(dt))
def price(self, asset_price, d1, strike_price, d2, risk_free_rate, dt):
        # Calculate NormalCDF for d1 & d2
        n1 = stats.norm.cdf(-d1)
        n2 = stats.norm.cdf(-d2)
        # Calculate call option price
        return strike_price*(math.exp(-(risk_free_rate*dt)))*n2 - asset_price*n1
def delta(self, d1):
        return stats.norm.cdf(d1) - 1
def exercise_prob(self):
        return stats.norm.cdf(((self.strike_price - self.asset_price) - (self.drift*self.asset_price*self.dt))/((self.volatility*self.asset_price)*(self.dt**.5)))
def __init__(self, asset_price, strike_price, volatility, expiration_date, risk_free_rate, drift):
        self.asset_price = asset_price
        self.strike_price = strike_price
        self.volatility = volatility
        self.expiration_date = expiration_date
        self.risk_free_rate = risk_free_rate
        self.drift = drift
        # Calculate delta t
        dt = np.busday_count(datetime.date.today(), expiration_date) / 252
        # Calculate d1
        d1 = self.d1(asset_price, strike_price, risk_free_rate, volatility, dt)
        # Calculate d2
        d2 = self.d2(d1, volatility, dt)
        self.dt = dt
        self.price = self.price(asset_price, d1, strike_price, d2, risk_free_rate, dt)
        self.delta = self.delta(d1)
        self.asset_price = asset_price

Теперь мы можем запускать этот код в бесконечном цикле с потоком данных из брокерской конторы, получая параметры каждый тик для анализа опционов в реальном времени. Подробнее об этой части процесса читайте в моей статье Разработка алгоритмической торговой системы. Это процесс алгоритмического дельта-хеджирования, поскольку дельта изменяется каждый тик (как показано на графике ниже), мы корректируем наше хеджирование, изменяя количество акций, которые у нас есть в базовом активе, чтобы соответствовать новой дельте. Однако в этом процессе есть фатальный недостаток, связанный с допущениями модели: отсутствие транзакционных издержек. Если мы будем корректировать хеджирование каждый тик, брокерская контора будет нести транзакционные издержки, что приведет к огромным убыткам.

# Adjust for delta t
class LiveOptionsGraph:
# Portfolio tick
    # Can be modified by appending new realtime data rather than randomly generated data
    def time_step(self, z):
        # Calculate dt so we can draw from a normal distribution to model the asset price
        dt = np.busday_count(datetime.date.today(), self.expiration_date) / 252
        if dt != 0:
            if(self.type == 'call'):
                eo = EuropeanCall(self.asset_prices[self.index] + np.random.normal(0, dt**(1/2)), self.strike_price, self.volatility, self.expiration_date, self.risk_free_rate, self.drift)
            elif(self.type == 'put'):
                eo = EuropeanPut(self.asset_prices[self.index] + np.random.normal(0, dt**(1/2)), self.strike_price, self.volatility, self.expiration_date, self.risk_free_rate, self.drift)
            self.option_prices.append(eo.price)
            self.deltas.append(eo.delta)
            self.index_set.append(self.index)
            self.axs[0].cla()
            self.axs[1].cla()
            self.axs[2].cla()
            self.axs[0].plot(self.index_set, self.option_prices, label='Black-Scholes Option Price', c='b')
            self.axs[1].plot(self.index_set, self.deltas, label='Delta', c='gray')
# Plot the asset price and strike price on the 3rd plot, green if in the money red if out of the money
            if self.type == 'call':
                if self.strike_price <= self.asset_prices[self.index]:
                    self.axs[2].plot(self.index_set, self.asset_prices, label='Asset Price', c='g')
                    self.axs[2].axhline(y=self.strike_price, label='Call Strike Price', c='gray')
                else:
                    self.axs[2].plot(self.index_set, self.asset_prices, label='Asset Price', c='r')
                    self.axs[2].axhline(y=self.strike_price, label='Call Strike Price', c='gray')
            elif self.type == 'put':
                if self.strike_price < self.asset_prices[self.index]:
                    self.axs[2].plot(self.index_set, self.asset_prices, label='Asset Price', c='r')
                    self.axs[2].axhline(y=self.strike_price, label='Put Strike Price', c='gray')
                else:
                    self.axs[2].plot(self.index_set, self.asset_prices, label='Asset Price', c='g')
                    self.axs[2].axhline(y=self.strike_price, label='Put Strike Price', c='gray')
self.axs[0].legend(loc='upper left')
            self.axs[1].legend(loc='upper left')
            self.axs[2].legend(loc='upper left')
            self.asset_prices.append(eo.asset_price)
            self.index = self.index + 1
            # Helps display time decay
            self.expiration_date = self.expiration_date - timedelta(days=1)
def __init__(self, european_option, type):
        self.index = 0
        self.asset_price = european_option.asset_price
        self.strike_price = european_option.strike_price
        self.volatility = european_option.volatility
        self.expiration_date = european_option.expiration_date
        self.risk_free_rate = european_option.risk_free_rate
        self.drift = european_option.drift
        self.type = type
        self.index_set = []
        self.option_prices = []
        self.asset_prices = [european_option.asset_price]
        self.deltas = []
        plt.style.use('dark_background')
        self.fig, self.axs = plt.subplots(3)
        self.ani = FuncAnimation(plt.gcf(), self.time_step, 100)
        plt.tight_layout()
        plt.show()
initial_ec = EuropeanCall(64.5, 65, .4, datetime.date(2020, 1, 31), .06, .2)
lg = LiveOptionsGraph(initial_ec, 'call')

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

Заключение

В этой статье показано алгоритмическое дельта-хеджирование с использованием модели Блэка-Шоулза и интуиции биномиальных деревьев для поддержания безрискового портфеля. Это очевидно, поскольку цена базового актива изменяется, изменяется и дельта опциона. Показано, что теоретически повторное хеджирование портфеля в непрерывное время - лучший способ поддерживать почти идеальное хеджирование, однако на практике постоянное повторное хеджирование невозможно из-за транзакционных издержек, связанных с корректировкой портфеля. Существует множество возможных решений, поскольку это лучше всего представляет проблему оптимизации. Одним из возможных решений может быть моделирование методом Монте-Карло набора вариантов для определения оптимальной точки повторного хеджирования путем оптимизации некоторой функции полезности. Другой вариант - объединить моделирование методом Монте-Карло с обучением с подкреплением для обучения нейронной сети торговле в оптимальной точке повторного хеджирования.