Usage

This page contains simple examples of pricing path-dependent options. For detailed documentation, refer to API References.

Installation

To obtain the latest version of qdpmc, use pip:

pip install qdpmc

Get started

Import modules

In [1]: import qdpmc as qm

In [2]: import numpy as np

Set up parameters

Before pricing products, specify parameters of Monte Carlo simulation and market dynamics.

# Simulation parameters
In [3]: mc = qm.MonteCarlo(batch_size=125, num_iter=1000)

# Black-Scholes dynamics
In [4]: bs = qm.BlackScholes(r=0.03, q=0, v=0.25, day_counter=252)

Single-barrier options

This example in the index page demonstrates the pricing of an up-and-out call option using qdpmc. To price an up-and-out put option, modify the associated codes:

# Specify an up-and-out call option
In [5]: up_out_call = qm.UpOut(
   ...:     spot=100,
   ...:     barrier=150,
   ...:     rebate=0,
   ...:     ob_days=np.linspace(1, 252, 252),
   ...:     payoff=qm.Payoff(
   ...:         qm.plain_vanilla,
   ...:         strike=100,
   ...:         option_type="call"
   ...:     )
   ...: )
   ...: 

# PV and Greek letters
In [6]: up_out_call.calc_value(mc, bs)
Out[6]: 6.435311521045284

Calculate Greeks

Request Greeks by specifying request_greeks in calc_value:

In [7]: up_out_call.calc_value(mc, bs, request_greeks=True)
Out[7]: 
{'PV': 6.430979966605954,
 'Delta': 0.1869394858552549,
 'Gamma': -0.007855675540751743,
 'Rho': 6.289757131357756,
 'Vega': -20.446636793799605,
 'Theta': 3.915152029320878e-05}

By default, the method uses central differences for Delta, Rho, and Vega. Change the steps and schemes by specifying the associated parameters.

In [8]: up_out_call.calc_value(mc, bs, request_greeks=True,
   ...:                        fd_steps={'ds':0.01, 'dr': 0.01, 'dv': 0.01},
   ...:                        fd_scheme={'ds': 'central', 'dr': 'forward',
   ...:                                   'dv': 'backward'})
   ...: 
Out[8]: 
{'PV': 6.402304146113466,
 'Delta': 0.18180875644449906,
 'Gamma': -0.025603082730435843,
 'Rho': 15.781041288053938,
 'Vega': -21.055319943466674,
 'Theta': 3.073285944913474e-05}

Re-use random numbers

By default, each time calc_value is run a different set of random numbers is used (a different entropy is used to initialize the random number generator. Check out this page for details about NumPy random number generator). To reuse the same set of random numbers, run the following codes:

In [9]: e = mc.most_recent_entropy

In [10]: up_out_call.calc_value(mc, bs, entropy=e)
Out[10]: 6.402304146113474

Time-varying barrier level

If a scalar is passed to barrier, the barrier is then assumed to be time-invariant. Pass an array to barrier to specify a time-varying barrier level. However, note that this array should match the length of ob_days.

In [11]: time_varying_barrier = np.linspace(120, 130, 252)

In [12]: time_varying_barrier_option = qm.UpOut(
   ....:     spot=100,
   ....:     barrier=time_varying_barrier,
   ....:     rebate=0,
   ....:     ob_days=np.linspace(1, 252, 252),
   ....:     payoff=qm.Payoff(
   ....:         qm.plain_vanilla,
   ....:         strike=100,
   ....:         option_type="call"
   ....:     )
   ....: )
   ....: 

In [13]: time_varying_barrier_option.calc_value(mc, bs)
Out[13]: 1.9411275446277678

Define payoff function

To customize a payoff function, define a Payoff:

In [14]: def dsf_payoff(s, k1, k2):
   ....:     call = qm.plain_vanilla(s, k1, 'call')
   ....:     put = qm.plain_vanilla(s, k2, 'put')
   ....:     return call + put
   ....: 

In [15]: dsf = qm.DoubleOut(
   ....:     spot=100,
   ....:     barrier_up=120,
   ....:     barrier_down=90,
   ....:     ob_days_up=np.linspace(1, 252, 252),
   ....:     ob_days_down=np.linspace(21, 252, 12),
   ....:     payoff=qm.Payoff(
   ....:         func=dsf_payoff,
   ....:         k1=100, k2=110
   ....:     ),
   ....:     rebate_up=3,
   ....:     rebate_down=2
   ....: )
   ....: 

In [16]: dsf.calc_value(mc, bs)
Out[16]: 3.2037753324400757