

# Down-And-Out Put


```python
import pyvacon.analytics as analytics
import datetime as dt
import pyvacon.tools.converter as converter
import pyvacon.tools.enums as enums
import pyvacon.marketdata.testdata as mkt_testdata
import pyvacon.instruments.testdata as ins_testdata
import math
import pandas as pd
from scipy.stats import norm
import pyvacon.marketdata.plot as mkt_plot #import module for plotting functionality
#the next lin is a jupyter internal command to show the matplotlib graphs within the notebook
%matplotlib inline
# alternative library for plots (offline version)
```

## Definition of a Down-And-Out Put


A down-and-out put belongs to the class of barrier options. It is a put option that ceases to exist if the underlying's spot prices reaches a barrier which is less that the current asset price.

## Down-And-Out Put Pricing Formulas


The value of a down-and-out put in the case of a barrier which es less than the strike price can be calculated subtracting the price of a down-and-in put from a plain vanilla put. In the case of a barrier which is greater than or equal to the strike price, the price of the down-and-out put is by definition zero. Because the value of a down-and-in put with a barrier greater or equal than the strike equals the value of a plain vanilla put option, the formula proves this finding.

$$p_{do} = p - p_{di}$$ 

The price of a down-and-in put $p_{di}$ is defined as:

$$p_{di}=-S_0N(-x_1)e^{-qT}+Ke^{-rT}N(-x_1+\sigma\sqrt{T})+S_0e^{-qT}(H/S_0)^{2\lambda}[N(y)-N(y_1)]-Ke^{-rT}(H/S_0)^{2\lambda-2}[N(y-\sigma\sqrt{T})-N(y_1-\sigma\sqrt{T})]$$

with $S_0$ as the underlying asset's spot price, $K$ as the option's strike price, $H$ as the option's barrier, $N(x)$ as the cumulative probability distribution function for a standardized normal distribution, $r$ as the risk-free interest rate, $q$ as the borrowing rate, $\sigma$ as the underlying's volatility and $T$ as the option's maturity as year fraction

where 

\begin{align}
\lambda &=\frac{r-q+\sigma^2/2}{\sigma^2} \\
\\
y &=\frac{\ln[H^2/(S_0K)]}{\sigma\sqrt{T}}+\lambda\sigma\sqrt{T} \\
\\
x_1 &=\frac{\ln(S_0/H)}{\sigma\sqrt{T}}+\lambda\sigma\sqrt{T} \\
\\
y_1 &=\frac{\ln(H/S_0)}{\sigma\sqrt{T}}+\lambda\sigma\sqrt{T} \\
\end{align}

The following code defines the formula to value an down-and-out put.


```python
def dopPricer(S0, K, H, r, sigma, T, q=0, t=0):
    _lambda = (r-q+sigma**2/2)/sigma**2
    y = (math.log(H**2/(S0*K)))/(sigma*math.sqrt(T-t))+_lambda*sigma*math.sqrt(T-t)
    x1 = (math.log(S0/H))/(sigma*math.sqrt(T-t))+_lambda*sigma*math.sqrt(T-t)
    y1 = (math.log(H/S0))/(sigma*math.sqrt(T-t))+_lambda*sigma*math.sqrt(T-t)
    pdi = -S0*norm.cdf(-x1)*math.exp(-q*(T-t))+K*math.exp(-r*(T-t))*norm.cdf(-x1+sigma*math.sqrt(T-t))+S0*math.exp(-q*(T-t))*(H/S0)**(2*_lambda)*(norm.cdf(y)-norm.cdf(y1))-K*math.exp(-r*(T-t))*(H/S0)**(2*_lambda-2)*(norm.cdf(y-sigma*math.sqrt(T-t))-norm.cdf(y1-sigma*math.sqrt(T-t)))
    d1= (math.log(S0/K)+(r+sigma**2/2)*(T-t))/(sigma*math.sqrt(T-t))
    d2 = d1-sigma*math.sqrt(T-t)
    p = -1*(S0*norm.cdf(-1*d1)-K*math.exp(-r*(T-t))*norm.cdf(-1*d2))
    pdo = p - pdi
    if H>=K:
        return 0
    if H<K and S0>H:
        return pdo
    if H<K and S0<=H:
        return 0
```

## Manual Pricing of a Down-And-Out Put Option

In the following code, we use the defined formulas above and calculate the price of a down-and-out put assuming a non-dividend paying stock.


```python
S0 = 50.0
K = 50.0
H = 40.0
r = 0.05
q = 0
sigma = 0.3
T = 1.0
t = 0

print('Down-and-out put price =',dopPricer(S0, K, H, r, sigma, T, q, t))
```

**Plotting the price profile of a down-and-out put**

The following code constructs a plot which shows the option price dependent on the spot price. Additionally, the further plot shows the delta function of the down-and-out put.


```python
#Create different spots
spots = []
n=0.5
while n<=100:
    spots.append(n)
    n=n+0.1

#Calculate option prices for the individual spots assuming everything else remains the same
prices = []
for s in range(len(spots)):
    prices.append(dopPricer(spots[s], K, H, r, sigma, T, q, t))

prices_list = {'Spots': spots, 
              'Prices': prices}

dop_prices = pd.DataFrame(prices_list, index = spots)    


# Delta    
nominator = []
for s in range(len(spots)):
    nominator.append(dopPricer(spots[s], K, H, r, sigma, T, q, t)-dopPricer(spots[s-1], K, H, r, sigma, T, q, t))

denominator = []
for s in range(len(spots)):
    denominator.append(spots[s]-spots[s-1])
    
value = []
for s in range(len(nominator)):
    value.append(nominator[s]/denominator[s])

#Alternative way to calculate the deltas
#deltas = analytics.vectorDouble()
#for i in range(len(spots)):
#    deltas.append(norm.cdf((math.log(spots[s]/K)+(r+sigma**2/2)*(T-t))/(sigma*math.sqrt(T-t)))-1)
   
delta_list = {'Spots': spots, 
              'Deltas': value}

dop_deltas = pd.DataFrame(delta_list, index = spots)    

# Plotting

fig, (dop, dop_delta) = mkt_plot.plt.subplots(1,2, figsize=(12,4),dpi=100,num=1)
dop.plot(dop_prices['Spots'], dop_prices['Prices'], 'k', label='Down-and-out put price')
dop.set_title('Down-and-out Put Prices')
dop.set_xlabel('Spot')
dop.set_ylabel('Price')
dop.axvline(x=K, label='Strike', ls= '--', c='g')
dop.axvline(x=H, label='Barrier', ls=':', c='r')
legend = dop.legend(loc='best', shadow=True, fontsize='medium')
    
#fig, cdi2 = mkt_plot.plt.subplots()
dop_delta.plot(dop_deltas['Spots'], dop_deltas['Deltas'], 'k', label='Down-and-out put delta')
dop_delta.set_title('Down-and-out Put Deltas')
dop_delta.set_xlabel('Spot')
dop_delta.set_ylabel('Price')
dop_delta.axvline(x=K, label='Strike', ls= '--', c='g')
dop_delta.axvline(x=H, label='Barrier', ls=':', c='r')
legend = dop_delta.legend(loc='best', shadow=True, fontsize='medium')
```

## Option Pricing using pyvacon

### Create the necessary market data
As a first step, we need to create the market data necessary to undertake the valuation. Therefore, we need to construct a *discount-*, *funding-* and *borrowing curve*, a *forward-curve* as well as a *volatility surface*.

#### Create a discount-, funding-, and borrowing-curve


```python
object_id = "TEST_DC"
refdate = analytics.ptime(2017,1,1,0,0,0)
days_to_maturity = [1, 180, 365, 720, 3*365, 4*365, 10*365]
dates = converter.createPTimeList(refdate, days_to_maturity)
# discount factors from constant rate
disc_rate = 0.05
borrow_rate = 0.0
funding_rate = 0.05

disc_df = analytics.vectorDouble()
for d in days_to_maturity:
    disc_df.append(math.exp(-d/365.0*disc_rate))
dc = analytics.DiscountCurve(object_id, refdate,dates, disc_df, enums.DayCounter.ACT365_FIXED, enums.InterpolationType.HAGAN_DF, enums.ExtrapolationType.NONE)

borrow_df = analytics.vectorDouble()
for d in days_to_maturity:
    borrow_df.append(math.exp(-d/365.0*borrow_rate))
bc = analytics.DiscountCurve(object_id, refdate, dates, borrow_df, enums.DayCounter.ACT365_FIXED, 
                             enums.InterpolationType.HAGAN_DF, enums.ExtrapolationType.NONE)

funding_df = analytics.vectorDouble()
for d in days_to_maturity:
    funding_df.append(math.exp(-d/365.0*funding_rate))
fc = analytics.DiscountCurve(object_id, refdate, dates, funding_df, enums.DayCounter.ACT365_FIXED, 
                             enums.InterpolationType.HAGAN_DF, enums.ExtrapolationType.NONE)
```

#### Create a forward curve
An equity forward curve has to be created to determine the future spot price. The procedure follows the description in the [equity forward curves](../marketdata/equity_forwardcurve.ipynb) notebook. To make the results comparable, we assume that the underlying is a non-dividend paying stock.


```python
# Creation of a Forward curve
#div table
object_id = "Div_Table"
refdate = analytics.ptime(2017,1,1,0,0,0) #dates which enters analytics objects must be analytics ptimes. 

ex_dates = converter.createPTimeList(refdate, [dt.datetime(2018,3,29), dt.datetime(2019,3,29), dt.datetime(2020,3,29), dt.datetime(2021,3,29)])
pay_dates = converter.createPTimeList(refdate, [dt.datetime(2018,4,1), dt.datetime(2019,4,1), dt.datetime(2020,4,1), dt.datetime(2021,4,1)])
tax_factors = analytics.vectorDouble([1.0, 1.0, 1.0, 1.0])
div_yield = analytics.vectorDouble([0, 0.00, 0.0, 0.0])
div_cash = analytics.vectorDouble([0.0, 0.0, 0.0, 0.0])

div_table=analytics.DividendTable(object_id, refdate, ex_dates, div_yield, div_cash, tax_factors, pay_dates)

spot = S0

forward_curve = analytics.EquityForwardCurve(refdate, spot, fc, bc, div_table)
```

#### Create a volatility surface
The procedure follows the description in the [equity volatilities](../marketdata/equity_volatilities.ipynb) notebook. In order to compare the results with the model above, we use a flat volatility surface.


```python
#expiries = [1.0/12.0, 1.0, 2.0, 3.0]
#atm_vols =  [0.3, 0.28, 0.25, 0.24]
#term_param = analytics.VolatilityParametrizationTerm(expiries,atm_vols)
flat_param = analytics.VolatilityParametrizationFlat(0.3)

obj_id = 'TEST_SURFACE'
vol_surf = analytics.VolatilitySurface(obj_id, refdate, forward_curve, enums.DayCounter.ACT365_FIXED, flat_param)

fwdspot = forward_curve.value(converter.getLTime(refdate),converter.getLTime(refdate))
```

### Setup the specification

The down-and-out put specification requires the following parameters:

- spot price of the underlying
- currency
- expiry relative to the reference date in days
- barrier level relative to the spot
- rebate level relative to the spot
- strike level relative to the spot
- underlying
- reference date
- barrier at start



```python
rel_expiry = 365 # expiry in number of days
rel_level = (H/spot) #barrier level relative to spot # Achtung, hier stand 0, was wird hier genau erwartet?
rel_rebate = 0 # rebate relative to spot
rel_strike = K/spot # strike relative to spot
spot=fwdspot
udl = 'DBK'
dop = ins_testdata.DOP.__create_DOP__(spot, 'EUR', rel_expiry, rel_level, rel_strike, rel_rebate, 
                                        udl,converter.getLTime(refdate))
#help(ins_testdata.DOP.__create_DOP__)
```

### Setup the pricing data
A product may be priced in two different ways: One may either fill the respective pricing data needed for a special pricer (which inherits from the BasePricingData) and use the respective price method where just the pricing data is given. Another possibility is to use the price-method where the storages are given. In this case, the pricer will fill the needed pricing data according to the underlying and other data as specified in the product specification.

**Here we show the approach explicitly setting the pricing data.**  


```python
dop_pricing_data = analytics.LocalVolPdePricingData()
#help(analytics.LocalVolPdePricingData)
dop_pricing_data.pricer = 'LocalVolPdePricer'
dop_pricing_data.valDate = converter.getLTime(refdate)
dop_pricing_data.pricingRequest = analytics.PricingRequest()
dop_pricing_data.vol = vol_surf
dop_pricing_data.spec = dop
dop_pricing_data.param = analytics.PdePricingParameter()
dop_pricing_data.dsc = dc
```

### Pricing

After all necessary information has been set, the price of the option can be calculated using the *analytics.price* function.


```python
pr = analytics.price(dop_pricing_data)
pr.getPrice()
```

---

7f4544f8704662b20ceb32e99cc496b89eeb420d
ipynb
Jupyter Notebook
instruments/dop.ipynb
frontmark/jupyter-notebooks
556a01bc4d3f2dc9b44af9c167aaabf35d23f5ba
MIT
2020-01-16T17:05:07.000Z
2022-02-17T00:59:00.000Z
instruments/dop.ipynb
frontmark/jupyter-notebooks
556a01bc4d3f2dc9b44af9c167aaabf35d23f5ba
MIT
2020-05-01T10:19:24.000Z
2020-10-07T10:40:03.000Z
instruments/dop.ipynb
frontmark/jupyter-notebooks
556a01bc4d3f2dc9b44af9c167aaabf35d23f5ba
MIT
2020-01-31T18:52:56.000Z
2022-03-27T12:06:01.000Z
Qwen/Qwen-72B
1. YES
2. YES