Продолжаем рассматривать алгоритмы построения улыбки волатильности. В этой статье будем находить «справедливые» цены опционов при помощи модели Хестона, которая относится к так называемым моделям стохастической волатильности. Хестон предложил использовать в качестве модели базового актива систему следующих уравнений:
где St,Vt- цена и волатильность базового актива соответственно, — случайные броуновские процессы с корреляцией ρ. Vt - это квадратичный процесс с возвратом к среднему (mean reverting) со средним значением θ и интенсивностью k. σ - среднеквадратичное отклонение волатильности, r — безрисковая ставка (для маржируемых равна 0, поэтому исключим сразу этот параметр для российского рынка).
Реальное статистическое распределение приращений цен базового актива плохо соответствует Гауссовскому распределению, на основе которого была получена формула Блэка-Шоулза. Модель Хестона может описывать разные стат. распределения, например, коэффициент ρ может быть интерпретирован как корелляция между логарифмом приращения цены и волатильностью актива, что позволяет учитывать эффект «толстых хвостов» распределения. График плотности распределения приращения цены с разными значениями ρ приведен в заглавии поста.
Цена европейского колл опциона для модели Хестона вычисляется по формуле:
, где
для j=1,2, где
Этот набор формул кажется сложным, однако решить их достаточно просто с помощью программы на C#, которая будет приведена ниже. Сложность составляет только вычисление интеграла с верхним бесконечным пределом в формуле для Pj, который находится с помощью числового метода Гаусса-Лагендре в той же программе. Также, для упрощения, можно сократить число параметров, убрав из них меру риска λ , применив риск-нейтральный подход. В этом случае:
Функция расчета формулы Хестона на языке C#:
//a-нижний предел интеграла (равен 0) //b - верхний предел интеграла. Выбирается значение от 100 до 200, в зависимости от нужной точности //delta - вычисляется грек дельта, который равен значению Р1 в формуле Хестона double HestonCallGaussLegendre(double S,double K,double T,double r,double kappa,double theta, double sigma,double lambda,double v0,double rho,int trap, double a, double b,ref double delta) { // Числовое интегрирование double[] int1=new double[32]; double[] int2 = new double[32]; double y; for (int k=0; k< =31; k++) { y = (a + b) / 2.0 + (b - a) / 2.0 * X[k]; int1[k] = W[k]*HestonCF(y,S,K,r,T,rho,kappa,theta,lambda,sigma,v0,1,trap); int2[k] = W[k]*HestonCF(y,S,K,r,T,rho,kappa,theta,lambda,sigma,v0,2,trap); } // Векторы для интегральной суммы double I1 = VectorSum(int1); double I2 = VectorSum(int2); // Определение Р1 и Р2 double P1 = 0.5 + 1.0/Math.PI*I1*(b-a)/2; double P2 = 0.5 + 1.0/Math.PI*I2*(b-a)/2; delta = P1; // Цена колл опциона return S*P1 - K*P2; } // Функция суммирования элементов вектора double VectorSum(double[] A) { double sum = 0; double n = A.Length; for (int i = 0; i <= n - 1; i++) sum += A[i]; return sum; } private double HestonCF(Complex phi, double Spot, double Strike, double Rate, double T, double Rho, double Kappa, double Theta, double Lambda, double Sigma, double V, int Pnum, int trap) { Complex S=new Complex(Spot , 0.0); // Цена базового актива Complex K=new Complex(Strike, 0.0); // Страйк Complex r=new Complex(Rate , 0.0); // Безрисковая ставка (для марж. опционов =0) Complex tau=new Complex(T , 0.0); // Период до экспирации в долях года Complex i=new Complex(0.0 , 1.0); // Мнимая переменная Complex rho=new Complex(Rho , 0.0); // Параметр Хестона: Корреляция Complex kappa = new Complex(Kappa, 0.0); // Параметр Хестона: Скорость возврата Complex theta = new Complex(Theta, 0.0); // Параметр Хестона: уровень возвратности Complex lambda = new Complex(Lambda, 0.0); // Параметр Хестона: мера риска (равна 0 для риск-нейтрального подхода) Complex sigma = new Complex(Sigma, 0.0); // Параметр Хестона: Среднеквадратичное волатильности Complex v0 = new Complex(V, 0.0); // Параметр Хестона: Текущая волатильность Complex two=new Complex(2.0 , 0.0); // число 2 в комплексной форме Complex one = new Complex(1.0, 0.0); // число 1 в комплексной форме Complex y, a, u, b, sigma2, d, g, G, C, D, c, f; y = rho*sigma*phi*i; a = kappa*theta; if (Pnum==1) { // Первая характеристическая функция u = 0.5; b = kappa + lambda - rho*sigma; } else { // Вторая характеристическая функция u = -0.5; b = kappa + lambda; } sigma2 = Complex.Pow(sigma,2); d = Complex.Sqrt((y-b)*(y-b) - sigma2*(two*u*phi*i - phi*phi)); g = (b - y + d)/(b - y - d); if (trap==1) { // Версия модели "Little Heston Trap" c = one/g; G = (one - c*Complex.Exp(-d*tau))/(one - c); C = r*i*phi*tau + a/sigma2*((b - rho*sigma*i*phi - d)*tau - two*Complex.Log(G)); D = (b - rho * sigma * i * phi - d) / sigma2 * ((one - Complex.Exp(-d * tau)) / (one - c * Complex.Exp(-d * tau))); } else { // Оригинальный вариант Хестона G = (one - g * Complex.Exp(d * tau)) / (one - g); C = r*i*phi*tau + a/sigma2*((b - rho*sigma*i*phi + d)*tau - two*Complex.Log(G)); D = (b - rho * sigma * i * phi + d) / sigma2 * ((one - Complex.Exp(d * tau)) / (one - g * Complex.Exp(d * tau))); } f = Complex.Exp(C + D*v0 + i*phi*Complex.Log(S)); // Вычисление реальной части подинтегрального выражения return (Complex.Exp(-i*phi*Complex.Log(K))*f/i/phi).Real; }
Следующий шаг — определение пяти параметров k,θ,σ,ρ,V(V — текущая волатильность). Для этого нужно откалибровать модель по наблюдаемым рыночным ценам опционов. Применяем стандартный метод — берем выборку цен для опционов разных страйков за определенный период времени (вместе со сроками до экспирации), при этом рыночной ценой опциона считаем среднюю цену между бидом и аском ( ), и минимизируем следующее выражение, применяя нелинейный метод наименьших квадратов (МНК):
где Ω - вектор параметров, wi - задаваемые веса (их выбор обсудим позже), N — размер выборки. Выражение в правой части означает, что полученные значения должны попадать в промежуток между бидом и аском наблюдаемых рыночных цен. Это ограничение, равно как и условие (волатильность не может падать до 0) позволяет сузить диапазон решений, полученных с помощью МНК. Таких решений может быть несколько из-за того, что МНК, попадая в локальный минимум выражения S(Ω), останавливается и выдает не оптимальные значения. Таким образом, нахождение оптимальных параметров модели Хестона является нетривиальной задачей, и применяются следующие способы ее решения:
Веса можно задать в соответствии с формулой: . Это интуитивный выбор, основанный на том, что, чем шире спред, тем больше свобода выбора в значении цены опциона. Для российского рынка лучшая аппроксимация получалась у меня при выборе одинакового значения весов, равного 1, но я не брал в рассмотрение слишком дальние страйки.
Получив параметры модели Хестона, мы сможем вычислить цены опционов C(Ki,Ti) для любого страйка и периода до экспирации. Для наглядности мы сможем построить улыбку волатильности по значениям подразумеваемой волатильности из формулы Блэка-Шоулза, подставив в нее хестоновские цены опционов — см. график в начале поста.
Модель Хестона отражает реальное статистическое распределение приращений цены базового актива значительно лучше, чем это делает модель Блэка-Шоулза, в чем вы сможете убедиться, сравнивая реальные рыночные цены опционов с полученными по этой модели. Однако у нее есть один существенный недостаток, который проявляется в том, что, если до экспирации остается небольшой срок (около недели для российского рынка) цены крайних страйков модель определяет неверно, в терминах подразумеваемой волатильности — хвосты улыбки начинают расходиться:
Чтобы устранить этот недостаток мы должны перейти к применению модифицированной модели Хестона — модели Бэйтса, являющейся одной из лучших аппроксимаций, позволяющих с макимальной точностью находить «справедливые» цены опционов. Ее мы рассмотрим в следующей части цикла статей про улыбку волатильности.
Другие стратегии, применяемые в алгоритмической торговле и биржевых роботах смотрите в моем блоге и на сайте.
А 'на-пальцах', в чем отличие модели Х? В том, что волатильность принята тоже как ф-ция стохастического процесса?
Думаю что такое объяснение и есть на пальцах: «коэффициент ρ может быть интерпретирован как корелляция между логарифмом приращения цены и волатильностью актива, что позволяет учитывать эффект «толстых хвостов» распределения».
«задавая различные значения начальных параметров, а затем выбрать минимальное» -
то метод «научного тыка» для минимизации.
К годным методам поиска минимума я бы добавил мои любимые генетические алгоритмы.
Еще есть симплекс-метод. Можно Попробовать его, давая случайные начальные координаты симплекса.
X[k] и W[k] откуда? Именно индексы, а не переменная Xk.
W = new double[32];
X = new double[32];
int info = 0;
alglib.gqgenerategausslegendre(32,out info, out X, out W);
32 — это количество узлов вычисления ( этой цифры достаточно для точности)
W — веса, X — узлы
Наезд на БШ ближе к концу статьи вообще-то не уместен. Это не проблема БШ, а проблема подгонки поверхности волатильности, о чем я уже говорил.