optcal.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. # -*- coding: utf-8 -*-
  2. from QuantLib import *
  3. from bs4 import BeautifulSoup
  4. from urllib2 import urlopen, Request
  5. from time import strftime
  6. import time
  7. import traceback
  8. def cal_implvol(spot, strike, callput, evaldate, exdate, rate, div, vol, premium):
  9. Settings.instance().evaluationDate = str2qdate(evaldate)
  10. exercise = EuropeanExercise(str2qdate(exdate))
  11. payoff = PlainVanillaPayoff(str2qopt_type(callput), strike)
  12. option = EuropeanOption(payoff,exercise)
  13. S = QuoteHandle(SimpleQuote(spot))
  14. # r = YieldTermStructureHandle(FlatForward(0, HongKong(), rate, Actual365Fixed()))
  15. # q = YieldTermStructureHandle(FlatForward(0, HongKong(), div, Actual365Fixed()))
  16. r = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), rate, Actual365Fixed()))
  17. q = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), div, Actual365Fixed()))
  18. sigma = BlackVolTermStructureHandle(BlackConstantVol(str2qdate(evaldate), HongKong(), vol, Actual365Fixed()))
  19. process = BlackScholesMertonProcess(S,q,r,sigma)
  20. im = option.impliedVolatility(premium, process)
  21. results = {}
  22. results['imvol'] = im
  23. return results
  24. def cal_option(spot, strike, callput, evaldate, exdate, rate, div, vol):
  25. Settings.instance().evaluationDate = str2qdate(evaldate)
  26. exercise = EuropeanExercise(str2qdate(exdate))
  27. payoff = PlainVanillaPayoff(str2qopt_type(callput), strike)
  28. option = EuropeanOption(payoff,exercise)
  29. S = QuoteHandle(SimpleQuote(spot))
  30. # r = YieldTermStructureHandle(FlatForward(0, HongKong(), rate, Actual365Fixed()))
  31. # q = YieldTermStructureHandle(FlatForward(0, HongKong(), div, Actual365Fixed()))
  32. r = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), rate, Actual365Fixed()))
  33. q = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), div, Actual365Fixed()))
  34. # sigma = BlackVolTermStructureHandle(BlackConstantVol(0, HongKong(), vol, Actual365Fixed()))
  35. sigma = BlackVolTermStructureHandle(BlackConstantVol(str2qdate(evaldate), HongKong(), vol, Actual365Fixed()))
  36. process = BlackScholesMertonProcess(S,q,r,sigma)
  37. engine = AnalyticEuropeanEngine(process)
  38. option.setPricingEngine(engine)
  39. results = {}
  40. results['npv'] = option.NPV()
  41. results['delta'] = option.delta()
  42. results['gamma'] = option.gamma()
  43. results['theta'] = option.theta() / 365
  44. results['vega'] = option.vega()
  45. # results['rho'] = option.rho()
  46. results['strikeSensitivity'] = option.strikeSensitivity()
  47. # results['thetaPerDay'] = option.thetaPerDay()
  48. # results['itmCashProbability'] = option.itmCashProbability()
  49. return results
  50. def str2qdate(yyyymmdd):
  51. months = [January, February, March, April, May, June, July, August, September, October,
  52. November, December]
  53. #print '%d%d%d'% (int(yyyymmdd[6:8]), int(yyyymmdd[4:6])-1 , int(yyyymmdd[0:4]))
  54. return Date(int(yyyymmdd[6:8]), months[int(yyyymmdd[4:6])-1 ], int(yyyymmdd[0:4]))
  55. def qdate2str(dd):
  56. return '%s%s%s' % (dd.ISO()[0:4], dd.ISO()[5:7], dd.ISO()[8:10])
  57. def str2qopt_type(callput):
  58. if callput.upper() == 'C':
  59. return Option.Call
  60. return Option.Put
  61. def get_hk_holidays(year):
  62. month_names = {'January' : 1,
  63. 'February': 2,
  64. 'March': 3,
  65. 'April': 4,
  66. 'May': 5,
  67. 'June': 6,
  68. 'July': 7,
  69. 'August': 8,
  70. 'September': 9,
  71. 'October': 10,
  72. 'November': 11,
  73. 'December': 12,
  74. }
  75. try:
  76. #print (Date().todaysDate() + Period("1y")).ISO()[0:4]
  77. if str(year) == (Date().todaysDate() + Period("1y")).ISO()[0:4]:
  78. url = 'http://www.gov.hk/en/about/abouthk/holiday/'
  79. else:
  80. #url = 'http://www.gov.hk/en/about/abouthk/holiday/{{year}}.htm'
  81. # lc 2017-01-03
  82. # just use the same url for both cases
  83. url = 'http://www.gov.hk/en/about/abouthk/holiday/'
  84. url = url.replace('{{year}}', str(year))
  85. headers = { 'User-Agent' : 'Mozilla/5.0' }
  86. req = Request(url, None, headers)
  87. html = urlopen(req).read()
  88. soup = BeautifulSoup(html, 'html5lib')
  89. tds = soup.findAll('h3')[0].parent.findAll('td', 'date')
  90. d1 = map(lambda x: (int(x.text.split(' ')[0]), x.text.split(' ')[1]), tds[1:])
  91. holidays = map(lambda x: '%d%02d%02d' % (year, int(month_names[x[1]]), int(x[0]) ), d1)
  92. #return map(lambda x: strftime('%Y%m%d', time.strptime('%s %s %s' % (month_names.index(x[1])+1, x[0], year), "%m %d %Y")), d1)
  93. #print d1
  94. print holidays
  95. return holidays
  96. except:
  97. traceback.print_exc()
  98. def get_HSI_last_trading_day(holidays, month, year):
  99. cal = HongKong()
  100. map(lambda x: cal.addHoliday(str2qdate(x)), holidays)
  101. def deduce_last_trading_day(ld):
  102. #print ld
  103. ld = ld - 1
  104. #print '###' + str(ld)
  105. if cal.isHoliday(ld):
  106. return deduce_last_trading_day(ld)
  107. elif not cal.isBusinessDay(ld):
  108. return deduce_last_trading_day(ld)
  109. else:
  110. #print '---' + str(ld-1)
  111. return ld
  112. def deduce_last_business_day(ld):
  113. #print ld
  114. #ld = ld - 1
  115. #print '###' + str(ld)
  116. if cal.isHoliday(ld):
  117. return deduce_last_business_day(ld - 1)
  118. elif not cal.isBusinessDay(ld):
  119. return deduce_last_business_day(ld - 1)
  120. else:
  121. #print '---' + str(ld-1)
  122. return ld
  123. return qdate2str(deduce_last_trading_day(deduce_last_business_day(Date.endOfMonth(Date(1, month, year)))))
  124. # QUANTLIB Period class usage:
  125. # https://ipythonquant.wordpress.com/2015/04/04/a-brief-introduction-to-the-quantlib-in-python/
  126. # check usage below:
  127. # def get_HSI_expiry(year):
  128. #
  129. # month_names = [January,
  130. # February,
  131. # March,
  132. # April,
  133. # May,
  134. # June,
  135. # July,
  136. # August,
  137. # September,
  138. # October,
  139. # November,
  140. # December,
  141. # ]
  142. # mm_dd = {}
  143. # # load calendar
  144. # holidays = ['20160101', '20160208', '20160209', '20160210', '20160325', '20160326', '20160328', '20160404', '20160502', '20160514', '20160609', '20160701', '20160916', '20161001', '20161010', '20161226', '20161227']
  145. # chk = HongKong()
  146. #
  147. #
  148. # map(lambda x: chk.addHoliday(Date(int(x[6:8]), month_names[int(x[4:6])-1], int(x[0:4]))), holidays)
  149. #
  150. #
  151. # #print Date.todaysDate() - 1
  152. # #chk.addHoliday(Date(24, December, 2015))
  153. # def deduce_last_trading_day(ld):
  154. # # ld -=1
  155. # ld = ld - 1
  156. #
  157. # if chk.isHoliday(ld):
  158. # return deduce_last_trading_day(ld - 1)
  159. # elif not chk.isBusinessDay(ld):
  160. # return deduce_last_trading_day(ld - 1)
  161. #
  162. # print ld
  163. # return ld
  164. #
  165. #
  166. #
  167. # return map(lambda x: deduce_last_trading_day(Date.endOfMonth(Date(1, x, year) + Period("1m"))), range(1, 12))
  168. # #mm_dd = deduce_last_trading_day(Date.endOfMonth(Date.todaysDate()))
  169. # #return mm_dd
  170. def test3(spot, strike, callput, evaldate, exdate, rate, div, vol):
  171. Settings.instance().evaluationDate = str2qdate(evaldate)
  172. exercise = EuropeanExercise(str2qdate(exdate))
  173. payoff = PlainVanillaPayoff(str2qopt_type(callput), strike)
  174. option = EuropeanOption(payoff,exercise)
  175. r = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), rate, Actual365Fixed()))
  176. q = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), div, Actual365Fixed()))
  177. S = QuoteHandle(SimpleQuote(spot))
  178. # r = YieldTermStructureHandle(FlatForward(0, HongKong(), rate, Actual365Fixed()))
  179. # q = YieldTermStructureHandle(FlatForward(0, HongKong(), div, Actual365Fixed()))
  180. # sigma = BlackVolTermStructureHandle(BlackConstantVol(0, HongKong(), vol, Actual365Fixed()))
  181. sigma = BlackVolTermStructureHandle(BlackConstantVol(str2qdate(evaldate), HongKong(), vol, Actual365Fixed()))
  182. process = BlackScholesMertonProcess(S,q,r,sigma)
  183. engine = AnalyticEuropeanEngine(process)
  184. option.setPricingEngine(engine)
  185. start_time = time.time()
  186. for i in range(100):
  187. results = cal_option(spot+i, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.120)
  188. results = {}
  189. results['npv'] = option.NPV()
  190. results['delta'] = option.delta()
  191. results['gamma'] = option.gamma()
  192. results['theta'] = option.theta() / 365
  193. results['vega'] = option.vega()
  194. # results['rho'] = option.rho()
  195. results['strikeSensitivity'] = option.strikeSensitivity()
  196. # results['thetaPerDay'] = option.thetaPerDay()
  197. # results['itmCashProbability'] = option.itmCashProbability()
  198. elapsed_time = time.time() - start_time
  199. print elapsed_time
  200. return results
  201. def test():
  202. start_time = time.time()
  203. results = cal_option(24290.0, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.120)
  204. elapsed_time = time.time() - start_time
  205. print 'elapsed time: %5.6f' % elapsed_time
  206. print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
  207. start_time = time.time()
  208. results = cal_implvol(24290.0, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.0, results['npv'])
  209. elapsed_time = time.time() - start_time
  210. print 'elapsed time: %5.6f' % elapsed_time
  211. print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
  212. print 'end of test 1'
  213. def test2():
  214. start_time = time.time()
  215. for i in range(100):
  216. results = cal_option(100+i, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.120)
  217. elapsed_time = time.time() - start_time
  218. print 'elapsed time: %5.6f' % elapsed_time
  219. print 'end of test 2'
  220. if __name__ == '__main__':
  221. #http://stackoverflow.com/questions/4891490/calculating-europeanoptionimpliedvolatility-in-quantlib-python
  222. # todaysDate = Date(10,August, 2015)
  223. # Settings.instance().evaluationDate = todaysDate
  224. #
  225. # exercise = EuropeanExercise(Date(28,August,2015))
  226. # payoff = PlainVanillaPayoff(Option.Call, 25600.0)
  227. # option = EuropeanOption(payoff,exercise)
  228. #
  229. # v = 0.19
  230. # S = QuoteHandle(SimpleQuote(24507.0))
  231. # r = YieldTermStructureHandle(FlatForward(0, TARGET(), 0.0005, Actual360()))
  232. # q = YieldTermStructureHandle(FlatForward(0, TARGET(), 0.0005, Actual360()))
  233. # sigma = BlackVolTermStructureHandle(BlackConstantVol(0, TARGET(), v, Actual360()))
  234. # process = BlackScholesMertonProcess(S,q,r,sigma)
  235. #
  236. #
  237. # im = option.impliedVolatility(85.0, process)
  238. # print im
  239. #
  240. # engine = AnalyticEuropeanEngine(process)
  241. # option.setPricingEngine(engine)
  242. # print '%0.4f delta=%0.4f gamma=%0.4f theta=%0.4f vega=%0.4f ' % (option.NPV(), option.delta(), option.gamma(), option.theta() / 360, option.vega())
  243. # for i in range(24000, 27000, 200):
  244. # results = cal_option(24189.0, i * 1.0, 'C', '20150804', '20150828', 0.0005, 0.0005, 0.19)
  245. # results2 = cal_option(24189.0, i * 1.0, 'P', '20150804', '20150828', 0.0005, 0.0005, 0.19)
  246. # print ('%d: '% i) + ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems()) + '|' + ''.join ('%s=%0.4f, '%(k,v) for k, v in results2.iteritems())
  247. # for k, v in results.iteritems():
  248. # print '%s= %0.4f' % (k, v)
  249. #spot 24119.0, X 25000, right: P, evaldate: 20150812, expiry: 20150828, rate: 0.0005, div: 0.0005, vol: 0.2000, premium: 334.0000
  250. #spot 24149.0, X 25200, right: P, evaldate: 20150812, expiry: 20150828, rate: 0.0005, div: 0.0005, vol: 0.2000, premium: 437.5000
  251. test()
  252. test2()
  253. test3(100, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.120)
  254. # results = cal_option(23067.0, 22000, 'P', '20151018', '20151029', 0.0009, 0.0328, 0.2918)
  255. # npv1 = results['npv']
  256. # v1 = 0.2918
  257. #
  258. #
  259. # print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
  260. # results = cal_option(23067.0, 22000, 'C', '20151018', '20151029', 0.0009, 0.0328, 0.2918)
  261. # print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
  262. #
  263. # results = cal_option(23067.0, 22000, 'P', '20151018', '20151029', 0.0009, 0.0328, 0.2918*1.01)
  264. # print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
  265. # npv2 = results['npv']
  266. # v2 = v1 * 1.01
  267. #
  268. # print 'validating vega: (%0.2f - %0.2f) / (%0.4f - %0.4f) = %0.2f' % (npv2, npv1, v2, v1, (npv2-npv1)/ (v2 - v1))
  269. # print 'validating gamma: (%0.2f - %0.2f) / (%0.4f - %0.4f) = %0.2f' % (npv2, npv1, v2, v1, (npv2-npv1)/ (v2 - v1))
  270. #
  271. # results = cal_option(23067.0, 22000, 'C', '20151018', '20151029', 0.0009, 0.0328, 0.2918*1.01)
  272. # print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
  273. #results = cal_implvol(22363.0, 22000, 'C', '20151201', '20151230', 0.0328, 0.00012, 0.21055,
  274. #print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
  275. #
  276. # chk = HongKong()
  277. # chk.addHoliday(Date(24, December, 2015))
  278. # chk.addHoliday(Date(19, October, 2015))
  279. # chk.addHoliday(str2qdate('20151020'))
  280. # print chk.isBusinessDay(Date(24, December, 2015))
  281. # print chk.isBusinessDay(Date(19, October, 2015))
  282. # print chk.isEndOfMonth(Date(29, October, 2015))
  283. # print chk.isEndOfMonth(Date(30, October, 2015))
  284. # print chk.advance(Date(17, October, 2015), 1, 0)
  285. # print chk.advance(Date(17, October, 2015), 1, 1)
  286. # print chk.advance(Date(17, October, 2015), 1, 2)
  287. #print get_HSI_expiry(2016)
  288. # holidays = get_hk_holidays(2017)
  289. #
  290. #
  291. #
  292. # month_names = [January,
  293. # February,
  294. # March,
  295. # April,
  296. # May,
  297. # June,
  298. # July,
  299. # August,
  300. # September,
  301. # October,
  302. # November,
  303. # December,
  304. # ]
  305. # for i in month_names:
  306. # dd = get_HSI_last_trading_day(holidays, i, 2017)
  307. # print dd
  308. #
  309. # print holidays
  310. # print get_HSI_last_trading_day(['20170128'], 1, 2017)