|
| 1 | +// Check that ceres works correctly |
| 2 | +#include <ceres/ceres.h> |
| 3 | +#include <gtest/gtest.h> |
| 4 | + |
| 5 | +#include <cmath> |
| 6 | +#include <iostream> |
| 7 | +#include <vector> |
| 8 | + |
| 9 | +template <typename T> |
| 10 | +T CalculateSABRVol(const T& alpha, const T& rho, const T& nu, double F, double K, double T_exp) { |
| 11 | + using std::abs; |
| 12 | + using std::log; |
| 13 | + using std::pow; |
| 14 | + using std::sqrt; |
| 15 | + |
| 16 | + const double beta = 1.0; |
| 17 | + const double eps = 1e-7; |
| 18 | + |
| 19 | + if (abs(F - K) < eps) { |
| 20 | + T sub_1 = T((1.0 - beta) * (1.0 - beta) / 24.0) * alpha * alpha / T(pow(F, 2.0 - 2.0 * beta)); |
| 21 | + T sub_2 = T(0.25 * rho * beta * nu) * alpha / T(pow(F, 1.0 - beta)); |
| 22 | + T sub_3 = (T(2.0) - T(3.0) * rho * rho) / T(24.0) * nu * nu; |
| 23 | + return (alpha / T(pow(F, 1.0 - beta))) * (T(1.0) + (sub_1 + sub_2 + sub_3) * T(T_exp)); |
| 24 | + } else { |
| 25 | + T logFK = T(log(F / K)); // Явное приведение double -> T |
| 26 | + T f_k_beta = T(pow(F * K, (1.0 - beta) / 2.0)); |
| 27 | + |
| 28 | + T z = (nu / alpha) * f_k_beta * logFK; |
| 29 | + T x_z = log((sqrt(T(1.0) - T(2.0) * rho * z + z * z) + z - rho) / (T(1.0) - rho)); |
| 30 | + |
| 31 | + T numer_1 = T((1.0 - beta) * (1.0 - beta) / 24.0) * alpha * alpha / pow(f_k_beta, 2.0); |
| 32 | + T numer_2 = T(0.25) * rho * T(beta) * nu * alpha / f_k_beta; |
| 33 | + T numer_3 = (T(2.0) - T(3.0) * rho * rho) / T(24.0) * nu * nu; |
| 34 | + |
| 35 | + T denum_1 = T((1.0 - beta) * (1.0 - beta) / 24.0) * logFK * logFK; |
| 36 | + T denum_2 = T(pow((1.0 - beta), 4.0) / 1920.0) * pow(logFK, 4.0); |
| 37 | + |
| 38 | + return (alpha / (f_k_beta * (T(1.0) + denum_1 + denum_2))) * (z / x_z) * |
| 39 | + (T(1.0) + (numer_1 + numer_2 + numer_3) * T(T_exp)); |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +// 2. Функтор для Ceres |
| 44 | +struct SabrCostFunctor { |
| 45 | + SabrCostFunctor(double market_vol, double F, double K, double T_exp) |
| 46 | + : m_vol(market_vol), m_F(F), m_K(K), m_T(T_exp) { |
| 47 | + } |
| 48 | + |
| 49 | + template <typename T> |
| 50 | + bool operator()(const T* const alpha, const T* const rho, const T* const nu, T* residual) const { |
| 51 | + // Вычисляем волатильность по модели |
| 52 | + T model_vol = CalculateSABRVol(alpha[0], rho[0], nu[0], m_F, m_K, m_T); |
| 53 | + // Ошибка (residual) |
| 54 | + residual[0] = model_vol - T(m_vol); |
| 55 | + return true; |
| 56 | + } |
| 57 | + |
| 58 | + const double m_vol, m_F, m_K, m_T; |
| 59 | +}; |
| 60 | + |
| 61 | +TEST(SABRProvider, CalibrationWithJet) { |
| 62 | + const double F = 100.0; |
| 63 | + const double K = 105.0; // Немного вне денег |
| 64 | + const double T = 1.0; |
| 65 | + const double market_vol = 0.22; |
| 66 | + |
| 67 | + double alpha = 0.2; |
| 68 | + double rho = -0.2; |
| 69 | + double nu = 0.4; |
| 70 | + |
| 71 | + ceres::Problem problem; |
| 72 | + |
| 73 | + problem.AddResidualBlock( |
| 74 | + new ceres::AutoDiffCostFunction<SabrCostFunctor, 1, 1, 1, 1>(new SabrCostFunctor(market_vol, F, K, T)), nullptr, |
| 75 | + &alpha, &rho, &nu); |
| 76 | + |
| 77 | + problem.SetParameterLowerBound(&alpha, 0, 0.001); |
| 78 | + problem.SetParameterLowerBound(&rho, 0, -0.99); |
| 79 | + problem.SetParameterUpperBound(&rho, 0, 0.99); |
| 80 | + problem.SetParameterLowerBound(&nu, 0, 0.001); |
| 81 | + |
| 82 | + ceres::Solver::Options options; |
| 83 | + options.max_num_iterations = 50; |
| 84 | + options.linear_solver_type = ceres::DENSE_QR; |
| 85 | + |
| 86 | + ceres::Solver::Summary summary; |
| 87 | + ceres::Solve(options, &problem, &summary); |
| 88 | + |
| 89 | + std::cout << "Solver report:\n" << summary.BriefReport() << "\n"; |
| 90 | + std::cout << "Final params: a=" << alpha << ", r=" << rho << ", n=" << nu << "\n"; |
| 91 | + |
| 92 | + EXPECT_TRUE(summary.IsSolutionUsable()); |
| 93 | + double final_vol = CalculateSABRVol(alpha, rho, nu, F, K, T); |
| 94 | + EXPECT_NEAR(final_vol, market_vol, 1e-6); |
| 95 | +} |
0 commit comments