autodiff_black_scholes.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // Copyright Matthew Pulver 2018 - 2019.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // https://www.boost.org/LICENSE_1_0.txt)
  5. #include <boost/math/differentiation/autodiff.hpp>
  6. #include <iostream>
  7. #include <stdexcept>
  8. using namespace boost::math::constants;
  9. using namespace boost::math::differentiation;
  10. // Equations and function/variable names are from
  11. // https://en.wikipedia.org/wiki/Greeks_(finance)#Formulas_for_European_option_Greeks
  12. // Standard normal probability density function
  13. template <typename X>
  14. X phi(X const& x) {
  15. return one_div_root_two_pi<X>() * exp(-0.5 * x * x);
  16. }
  17. // Standard normal cumulative distribution function
  18. template <typename X>
  19. X Phi(X const& x) {
  20. return 0.5 * erfc(-one_div_root_two<X>() * x);
  21. }
  22. enum class CP { call, put };
  23. // Assume zero annual dividend yield (q=0).
  24. template <typename Price, typename Sigma, typename Tau, typename Rate>
  25. promote<Price, Sigma, Tau, Rate> black_scholes_option_price(CP cp,
  26. double K,
  27. Price const& S,
  28. Sigma const& sigma,
  29. Tau const& tau,
  30. Rate const& r) {
  31. using namespace std;
  32. auto const d1 = (log(S / K) + (r + sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
  33. auto const d2 = (log(S / K) + (r - sigma * sigma / 2) * tau) / (sigma * sqrt(tau));
  34. switch (cp) {
  35. case CP::call:
  36. return S * Phi(d1) - exp(-r * tau) * K * Phi(d2);
  37. case CP::put:
  38. return exp(-r * tau) * K * Phi(-d2) - S * Phi(-d1);
  39. default:
  40. throw std::runtime_error("Invalid CP value.");
  41. }
  42. }
  43. int main() {
  44. double const K = 100.0; // Strike price.
  45. auto const variables = make_ftuple<double, 3, 3, 1, 1>(105, 5, 30.0 / 365, 1.25 / 100);
  46. auto const& S = std::get<0>(variables); // Stock price.
  47. auto const& sigma = std::get<1>(variables); // Volatility.
  48. auto const& tau = std::get<2>(variables); // Time to expiration in years. (30 days).
  49. auto const& r = std::get<3>(variables); // Interest rate.
  50. auto const call_price = black_scholes_option_price(CP::call, K, S, sigma, tau, r);
  51. auto const put_price = black_scholes_option_price(CP::put, K, S, sigma, tau, r);
  52. double const d1 = static_cast<double>((log(S / K) + (r + sigma * sigma / 2) * tau) / (sigma * sqrt(tau)));
  53. double const d2 = static_cast<double>((log(S / K) + (r - sigma * sigma / 2) * tau) / (sigma * sqrt(tau)));
  54. double const formula_call_delta = +Phi(+d1);
  55. double const formula_put_delta = -Phi(-d1);
  56. double const formula_vega = static_cast<double>(S * phi(d1) * sqrt(tau));
  57. double const formula_call_theta =
  58. static_cast<double>(-S * phi(d1) * sigma / (2 * sqrt(tau)) - r * K * exp(-r * tau) * Phi(+d2));
  59. double const formula_put_theta =
  60. static_cast<double>(-S * phi(d1) * sigma / (2 * sqrt(tau)) + r * K * exp(-r * tau) * Phi(-d2));
  61. double const formula_call_rho = static_cast<double>(+K * tau * exp(-r * tau) * Phi(+d2));
  62. double const formula_put_rho = static_cast<double>(-K * tau * exp(-r * tau) * Phi(-d2));
  63. double const formula_gamma = static_cast<double>(phi(d1) / (S * sigma * sqrt(tau)));
  64. double const formula_vanna = static_cast<double>(-phi(d1) * d2 / sigma);
  65. double const formula_charm =
  66. static_cast<double>(phi(d1) * (d2 * sigma * sqrt(tau) - 2 * r * tau) / (2 * tau * sigma * sqrt(tau)));
  67. double const formula_vomma = static_cast<double>(S * phi(d1) * sqrt(tau) * d1 * d2 / sigma);
  68. double const formula_veta = static_cast<double>(-S * phi(d1) * sqrt(tau) *
  69. (r * d1 / (sigma * sqrt(tau)) - (1 + d1 * d2) / (2 * tau)));
  70. double const formula_speed =
  71. static_cast<double>(-phi(d1) * (d1 / (sigma * sqrt(tau)) + 1) / (S * S * sigma * sqrt(tau)));
  72. double const formula_zomma = static_cast<double>(phi(d1) * (d1 * d2 - 1) / (S * sigma * sigma * sqrt(tau)));
  73. double const formula_color =
  74. static_cast<double>(-phi(d1) / (2 * S * tau * sigma * sqrt(tau)) *
  75. (1 + (2 * r * tau - d2 * sigma * sqrt(tau)) * d1 / (sigma * sqrt(tau))));
  76. double const formula_ultima =
  77. -formula_vega * static_cast<double>((d1 * d2 * (1 - d1 * d2) + d1 * d1 + d2 * d2) / (sigma * sigma));
  78. std::cout << std::setprecision(std::numeric_limits<double>::digits10)
  79. << "autodiff black-scholes call price = " << call_price.derivative(0, 0, 0, 0) << '\n'
  80. << "autodiff black-scholes put price = " << put_price.derivative(0, 0, 0, 0) << '\n'
  81. << "\n## First-order Greeks\n"
  82. << "autodiff call delta = " << call_price.derivative(1, 0, 0, 0) << '\n'
  83. << " formula call delta = " << formula_call_delta << '\n'
  84. << "autodiff call vega = " << call_price.derivative(0, 1, 0, 0) << '\n'
  85. << " formula call vega = " << formula_vega << '\n'
  86. << "autodiff call theta = " << -call_price.derivative(0, 0, 1, 0)
  87. << '\n' // minus sign due to tau = T-time
  88. << " formula call theta = " << formula_call_theta << '\n'
  89. << "autodiff call rho = " << call_price.derivative(0, 0, 0, 1) << '\n'
  90. << " formula call rho = " << formula_call_rho << '\n'
  91. << '\n'
  92. << "autodiff put delta = " << put_price.derivative(1, 0, 0, 0) << '\n'
  93. << " formula put delta = " << formula_put_delta << '\n'
  94. << "autodiff put vega = " << put_price.derivative(0, 1, 0, 0) << '\n'
  95. << " formula put vega = " << formula_vega << '\n'
  96. << "autodiff put theta = " << -put_price.derivative(0, 0, 1, 0) << '\n'
  97. << " formula put theta = " << formula_put_theta << '\n'
  98. << "autodiff put rho = " << put_price.derivative(0, 0, 0, 1) << '\n'
  99. << " formula put rho = " << formula_put_rho << '\n'
  100. << "\n## Second-order Greeks\n"
  101. << "autodiff call gamma = " << call_price.derivative(2, 0, 0, 0) << '\n'
  102. << "autodiff put gamma = " << put_price.derivative(2, 0, 0, 0) << '\n'
  103. << " formula gamma = " << formula_gamma << '\n'
  104. << "autodiff call vanna = " << call_price.derivative(1, 1, 0, 0) << '\n'
  105. << "autodiff put vanna = " << put_price.derivative(1, 1, 0, 0) << '\n'
  106. << " formula vanna = " << formula_vanna << '\n'
  107. << "autodiff call charm = " << -call_price.derivative(1, 0, 1, 0) << '\n'
  108. << "autodiff put charm = " << -put_price.derivative(1, 0, 1, 0) << '\n'
  109. << " formula charm = " << formula_charm << '\n'
  110. << "autodiff call vomma = " << call_price.derivative(0, 2, 0, 0) << '\n'
  111. << "autodiff put vomma = " << put_price.derivative(0, 2, 0, 0) << '\n'
  112. << " formula vomma = " << formula_vomma << '\n'
  113. << "autodiff call veta = " << call_price.derivative(0, 1, 1, 0) << '\n'
  114. << "autodiff put veta = " << put_price.derivative(0, 1, 1, 0) << '\n'
  115. << " formula veta = " << formula_veta << '\n'
  116. << "\n## Third-order Greeks\n"
  117. << "autodiff call speed = " << call_price.derivative(3, 0, 0, 0) << '\n'
  118. << "autodiff put speed = " << put_price.derivative(3, 0, 0, 0) << '\n'
  119. << " formula speed = " << formula_speed << '\n'
  120. << "autodiff call zomma = " << call_price.derivative(2, 1, 0, 0) << '\n'
  121. << "autodiff put zomma = " << put_price.derivative(2, 1, 0, 0) << '\n'
  122. << " formula zomma = " << formula_zomma << '\n'
  123. << "autodiff call color = " << call_price.derivative(2, 0, 1, 0) << '\n'
  124. << "autodiff put color = " << put_price.derivative(2, 0, 1, 0) << '\n'
  125. << " formula color = " << formula_color << '\n'
  126. << "autodiff call ultima = " << call_price.derivative(0, 3, 0, 0) << '\n'
  127. << "autodiff put ultima = " << put_price.derivative(0, 3, 0, 0) << '\n'
  128. << " formula ultima = " << formula_ultima << '\n';
  129. return 0;
  130. }
  131. /*
  132. Output:
  133. autodiff black-scholes call price = 56.5136030677739
  134. autodiff black-scholes put price = 51.4109161009333
  135. ## First-order Greeks
  136. autodiff call delta = 0.773818444921273
  137. formula call delta = 0.773818444921274
  138. autodiff call vega = 9.05493427705736
  139. formula call vega = 9.05493427705736
  140. autodiff call theta = -275.73013426444
  141. formula call theta = -275.73013426444
  142. autodiff call rho = 2.03320550539396
  143. formula call rho = 2.03320550539396
  144. autodiff put delta = -0.226181555078726
  145. formula put delta = -0.226181555078726
  146. autodiff put vega = 9.05493427705736
  147. formula put vega = 9.05493427705736
  148. autodiff put theta = -274.481417851526
  149. formula put theta = -274.481417851526
  150. autodiff put rho = -6.17753255212599
  151. formula put rho = -6.17753255212599
  152. ## Second-order Greeks
  153. autodiff call gamma = 0.00199851912993254
  154. autodiff put gamma = 0.00199851912993254
  155. formula gamma = 0.00199851912993254
  156. autodiff call vanna = 0.0410279463126531
  157. autodiff put vanna = 0.0410279463126531
  158. formula vanna = 0.0410279463126531
  159. autodiff call charm = -1.2505564233679
  160. autodiff put charm = -1.2505564233679
  161. formula charm = -1.2505564233679
  162. autodiff call vomma = -0.928114149313108
  163. autodiff put vomma = -0.928114149313108
  164. formula vomma = -0.928114149313107
  165. autodiff call veta = 26.7947073115641
  166. autodiff put veta = 26.7947073115641
  167. formula veta = 26.7947073115641
  168. ## Third-order Greeks
  169. autodiff call speed = -2.90117322380992e-05
  170. autodiff put speed = -2.90117322380992e-05
  171. formula speed = -2.90117322380992e-05
  172. autodiff call zomma = -0.000604548369901419
  173. autodiff put zomma = -0.000604548369901419
  174. formula zomma = -0.000604548369901419
  175. autodiff call color = -0.0184014426606065
  176. autodiff put color = -0.0184014426606065
  177. formula color = -0.0184014426606065
  178. autodiff call ultima = -0.0922426864775683
  179. autodiff put ultima = -0.0922426864775683
  180. formula ultima = -0.0922426864775685
  181. **/