├── .gitignore
├── BinomialModel
├── BinomialPricing.py
└── __init__.py
├── BlackAndScholes
├── BSPricing.py
├── Greeks.py
└── __init__.py
├── CNAME
├── DataRequest
├── __init__.py
├── y_finane_option_data.py
└── y_finane_stock_data.py
├── Enum
├── BuySellSide.py
├── OptionType.py
└── __init__.py
├── Images
├── CallSkew.png
├── Delta.png
├── DupireVolVsBlack.png
├── Gamma.png
├── Heston Vol.png
├── IVImpactCallValue.png
├── IV_CALL.png
├── IV_PUT.png
├── IV_vs_LV.png
├── LongButterfly.png
├── MktPriceCP.png
├── OTM_IV.png
├── PutSkew.png
├── Put_CP.png
├── ShortButterFly.png
├── SyntheticCall.png
├── SyntheticPUT.png
├── TSLA Price.png
├── TSLA Vol.png
├── TSLA returns.png
├── Theta.png
├── TimeImpact.png
├── Vega.png
├── callSpread.png
├── calls_CP.png
├── dataFrame_TSLA.png
├── localVolatility.png
├── putSpread8090.png
├── straddle.png
└── strangle.png
├── Option
├── Option.py
├── OptionStrategies.py
└── __init__.py
├── README.md
├── Volatility
├── DupireVolatility.py
├── ImpliedVolatiliy.py
├── StochasticVolatility.py
├── __init__.py
└── hestonModel.py
├── main.py
├── mainCallPutParity.py
├── mainHestonSimulation.py
├── mainImpliedVolatility.py
├── mainOptionStrategies.py
└── mainPricingModel.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *pyc
2 | *.pyc
3 | *.idea
4 |
--------------------------------------------------------------------------------
/BinomialModel/BinomialPricing.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import pandas_datareader.data as web
3 | import numpy as np
4 | import datetime as dt
5 | import math
6 |
7 | import matplotlib.pyplot as plt
8 |
9 |
10 | def combos(n, i):
11 | return math.factorial(n) / (math.factorial(n - i) * math.factorial(i))
12 |
13 |
14 | def binom_EU1(S0, K, T, r, sigma, N, type_='call'):
15 | dt = T / N
16 | u = np.exp(sigma * np.sqrt(dt))
17 | d = np.exp(-sigma * np.sqrt(dt))
18 | p = (np.exp(r * dt) - d) / (u - d)
19 | value = 0
20 | for i in range(N + 1):
21 | node_prob = combos(N, i) * p ** i * (1 - p) ** (N - i)
22 | ST = S0 * (u) ** i * (d) ** (N - i)
23 | if type_ == 'CALL':
24 | value += max(ST - K, 0) * node_prob
25 | elif type_ == 'PUT':
26 | value += max(K - ST, 0) * node_prob
27 | else:
28 | raise ValueError("type_ must be 'call' or 'put'")
29 |
30 | return value * np.exp(-r * T)
31 |
32 |
--------------------------------------------------------------------------------
/BinomialModel/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/BinomialModel/__init__.py
--------------------------------------------------------------------------------
/BlackAndScholes/BSPricing.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from scipy.stats import norm
3 | import Option.Option as Option
4 | N = norm.cdf
5 |
6 |
7 | def BS_CALL(S, K, T, r, sigma):
8 | d1 = (np.log(S/K) + (r + sigma**2/2)*T) / (sigma*np.sqrt(T))
9 | d2 = d1 - sigma * np.sqrt(T)
10 | return S * N(d1) - K * np.exp(-r*T)* N(d2)
11 |
12 |
13 | def BS_PUT(S, K, T, r, sigma):
14 | d1 = (np.log(S/K) + (r + sigma**2/2)*T) / (sigma*np.sqrt(T))
15 | d2 = d1 - sigma* np.sqrt(T)
16 | return K* np.exp(-r*T) * N(-d2) - S*N(-d1)
17 |
18 |
19 | def priceBS(S, K, T, r, sigma ,type='CALL'):
20 | if type == 'CALL':
21 | return BS_CALL(S, K, T, r, sigma)
22 | if type == 'PUT':
23 | return BS_PUT(S, K, T, r, sigma)
24 | else:
25 | raise ValueError('Unrecognized type')
26 |
27 |
28 |
--------------------------------------------------------------------------------
/BlackAndScholes/Greeks.py:
--------------------------------------------------------------------------------
1 | from scipy.stats import norm
2 | from Enum.OptionType import OpionType
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 |
6 | N = norm.cdf
7 | N_prime = norm.pdf
8 |
9 | def d1(S, K, T, r, sigma) -> float:
10 | return (np.log(S/K) + (r + sigma**2/2)*T) /\
11 | sigma*np.sqrt(T)
12 |
13 |
14 | def d2(S, K, T, r, sigma) -> float:
15 | return d1(S, K, T, r, sigma) - sigma* np.sqrt(T)
16 |
17 | def Delta( S, K , T , r , sigma, type:OpionType) -> float:
18 | if type == OpionType.CALL:
19 | return N(d1(S, K, T, r, sigma))
20 | elif type == OpionType.PUT:
21 | return - N(-d1(S, K, T, r, sigma))
22 |
23 | def Gamma(S, K, T, r, sigma) -> float:
24 | N_prime = norm.pdf
25 | return N_prime(d1(S,K, T, r, sigma))/(S*sigma*np.sqrt(T))
26 |
27 | def Vega(S, K, T, r, sigma) -> float:
28 | return S*np.sqrt(T)*N_prime(d1(S,K,T,r,sigma))
29 |
30 | def Theta(S, K, T, r, sigma , type:OpionType) -> float:
31 | if type == OpionType.CALL:
32 | p1 = - S*N_prime(d1(S, K, T, r, sigma))*sigma / (2 * np.sqrt(T))
33 | p2 = r*K*np.exp(-r*T)*N(d2(S, K, T, r, sigma))
34 | return p1 - p2
35 | elif type == OpionType.PUT:
36 | p1 = - S * N_prime(d1(S, K, T, r, sigma)) * sigma / (2 * np.sqrt(T))
37 | p2 = r * K * np.exp(-r * T) * N(-d2(S, K, T, r, sigma))
38 | return p1 + p2
39 |
40 | def Rho(S, K, T, r, sigma , type:OpionType) -> float:
41 | if type == OpionType.CALL:
42 | return K * T * np.exp(-r * T) * N(d2(S, K, T, r, sigma))
43 | elif type == OpionType.PUT:
44 | return -K * T * np.exp(-r * T) * N(-d2(S, K, T, r, sigma))
45 |
46 | #Example
47 | S = 100
48 | K = 100
49 | T = 1
50 | r = 0.00
51 | sigma = 0.25
52 |
53 | prices = np.arange(1, 250,1)
54 |
55 | deltas_c = Delta(prices, K, T, r, sigma , OpionType.CALL)
--------------------------------------------------------------------------------
/BlackAndScholes/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/BlackAndScholes/__init__.py
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | mytest.adriencassagnes.com
--------------------------------------------------------------------------------
/DataRequest/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/DataRequest/__init__.py
--------------------------------------------------------------------------------
/DataRequest/y_finane_option_data.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import yfinance as yf
3 | import datetime
4 |
5 |
6 | def get_option_data(symbol):
7 | tk = yf.Ticker(symbol)
8 | expirations = tk.options
9 |
10 | options = pd.DataFrame()
11 |
12 | for e in expirations:
13 | opt = tk.option_chain(e)
14 | opt = pd.concat([opt.calls,opt.puts])
15 | opt['expirationDate'] = e
16 | options = pd.concat([options, opt] ,ignore_index=True)
17 |
18 | options['expirationDate'] = pd.to_datetime(options['expirationDate'])
19 | options['T_days'] = (options['expirationDate'] - datetime.datetime.today()).dt.days
20 | options['T_days'] = options['T_days'].apply(lambda x: 0 if x < 0 else x)
21 | options['Type'] = options['contractSymbol'].str[4:].apply(lambda x: "CALL" if 'C' in x else "PUT" )
22 | options[['bid', 'ask', 'strike']] = options[['bid', 'ask', 'strike']].apply(pd.to_numeric)
23 |
24 | return options
25 |
26 |
27 | def get_stock_price(ticker):
28 | data = yf.download(ticker)
29 | return pd.DataFrame(data)
30 |
31 |
32 | def get_Option_Data_full(symbol) -> pd.DataFrame:
33 | print('getting options quotes..')
34 | option_df = get_option_data(symbol)
35 | print('getting stock price..')
36 | stockPrices_ = get_stock_price(symbol)
37 |
38 | last_price = stockPrices_.tail(1)['Adj Close'].values[0]
39 | option_df['underlying_LastPrice'] = last_price
40 | option_df['mid_price'] = (option_df['bid'] + option_df['ask']) /2
41 |
42 | return option_df
43 |
44 |
--------------------------------------------------------------------------------
/DataRequest/y_finane_stock_data.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import yfinance as yf
3 |
4 |
5 |
6 | def get_stock_price(ticker):
7 | data = yf.download(ticker)
8 | return pd.DataFrame(data)
9 |
--------------------------------------------------------------------------------
/Enum/BuySellSide.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class BuySellSide(Enum):
5 | NONE = 0
6 | BUY = 1
7 | SELL = -1
8 |
--------------------------------------------------------------------------------
/Enum/OptionType.py:
--------------------------------------------------------------------------------
1 | from enum import Enum
2 |
3 |
4 | class OpionType(Enum):
5 | NONE = 0
6 | PUT = 1
7 | CALL = 2
8 |
--------------------------------------------------------------------------------
/Enum/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Enum/__init__.py
--------------------------------------------------------------------------------
/Images/CallSkew.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/CallSkew.png
--------------------------------------------------------------------------------
/Images/Delta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/Delta.png
--------------------------------------------------------------------------------
/Images/DupireVolVsBlack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/DupireVolVsBlack.png
--------------------------------------------------------------------------------
/Images/Gamma.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/Gamma.png
--------------------------------------------------------------------------------
/Images/Heston Vol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/Heston Vol.png
--------------------------------------------------------------------------------
/Images/IVImpactCallValue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/IVImpactCallValue.png
--------------------------------------------------------------------------------
/Images/IV_CALL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/IV_CALL.png
--------------------------------------------------------------------------------
/Images/IV_PUT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/IV_PUT.png
--------------------------------------------------------------------------------
/Images/IV_vs_LV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/IV_vs_LV.png
--------------------------------------------------------------------------------
/Images/LongButterfly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/LongButterfly.png
--------------------------------------------------------------------------------
/Images/MktPriceCP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/MktPriceCP.png
--------------------------------------------------------------------------------
/Images/OTM_IV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/OTM_IV.png
--------------------------------------------------------------------------------
/Images/PutSkew.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/PutSkew.png
--------------------------------------------------------------------------------
/Images/Put_CP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/Put_CP.png
--------------------------------------------------------------------------------
/Images/ShortButterFly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/ShortButterFly.png
--------------------------------------------------------------------------------
/Images/SyntheticCall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/SyntheticCall.png
--------------------------------------------------------------------------------
/Images/SyntheticPUT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/SyntheticPUT.png
--------------------------------------------------------------------------------
/Images/TSLA Price.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/TSLA Price.png
--------------------------------------------------------------------------------
/Images/TSLA Vol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/TSLA Vol.png
--------------------------------------------------------------------------------
/Images/TSLA returns.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/TSLA returns.png
--------------------------------------------------------------------------------
/Images/Theta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/Theta.png
--------------------------------------------------------------------------------
/Images/TimeImpact.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/TimeImpact.png
--------------------------------------------------------------------------------
/Images/Vega.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/Vega.png
--------------------------------------------------------------------------------
/Images/callSpread.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/callSpread.png
--------------------------------------------------------------------------------
/Images/calls_CP.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/calls_CP.png
--------------------------------------------------------------------------------
/Images/dataFrame_TSLA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/dataFrame_TSLA.png
--------------------------------------------------------------------------------
/Images/localVolatility.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/localVolatility.png
--------------------------------------------------------------------------------
/Images/putSpread8090.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/putSpread8090.png
--------------------------------------------------------------------------------
/Images/straddle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/straddle.png
--------------------------------------------------------------------------------
/Images/strangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Images/strangle.png
--------------------------------------------------------------------------------
/Option/Option.py:
--------------------------------------------------------------------------------
1 | from Enum import OptionType
2 |
3 | class Option:
4 | def __init__(self, type : OptionType, K, price):
5 | self.type = type
6 | self.K = K
7 | self.price = price
8 |
9 | class Stock:
10 | def __init__(self , price ):
11 | self.price= price
--------------------------------------------------------------------------------
/Option/OptionStrategies.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from Option.Option import Option ,Stock
3 | import matplotlib.pyplot as plt
4 | from Enum.BuySellSide import BuySellSide
5 | from Enum.OptionType import OpionType
6 | from BlackAndScholes import Greeks
7 | from typing import Union
8 |
9 | class OptionStrategies:
10 | """
11 | the purpose of this class is to implement option trading strategies on one and the same underlying
12 | """
13 | def __init__(self, name, St):
14 | self.name = name
15 | self.St = St
16 | self.STs = np.arange(1, St * 2, 1)
17 | self.payoffs = np.zeros_like(self.STs)
18 | self.instruments = []
19 | self.side = []
20 |
21 | self.gamma=np.zeros_like(self.STs)
22 | self.delta=np.zeros_like(self.STs)
23 | self.theta=np.zeros_like(self.STs)
24 | self.vega=np.zeros_like(self.STs)
25 |
26 |
27 | def add_Option(self , option : Option , buySell:BuySellSide , option_number = 1) -> None:
28 |
29 | for nb in range(1 , option_number+1):
30 | self.instruments.append(option)
31 | self.side.append(buySell.value)
32 |
33 | self.payoffs = self.payoffs + self._payoff(option,buySell,option_number)
34 |
35 | print(f"{str(option_number)} option(s) added ")
36 |
37 | def add_deltaOne(self, stock:Stock, buySell:BuySellSide, stock_number=1) -> None:
38 |
39 | for nb in range(1 , stock_number+1):
40 | self.instruments.append(stock) ## add later stockPrice
41 |
42 | self.payoffs = self.payoffs + self._payoff(stock,buySell,stock_number)
43 |
44 | print(f"{str(stock_number)} option(s) added ")
45 |
46 | def _payoff(self , instr :Union[Option , Stock] , side:BuySellSide , number):
47 |
48 | payoff= None
49 | if(type(instr) == Option):
50 | option = instr
51 | K = option.K
52 | if side == BuySellSide.BUY:
53 | if option.type ==OpionType.CALL:
54 | payoff = np.array([max(S - K, 0) - option.price for S in self.STs]) * number
55 | elif option.type == OpionType.PUT:
56 | payoff = np.array([max(K - S, 0) - option.price for S in self.STs]) * number
57 |
58 | elif side == BuySellSide.SELL:
59 | if option.type == OpionType.CALL:
60 | payoff = np.array([- (max(S - K, 0) - option.price )for S in self.STs]) * number
61 | elif option.type == OpionType.PUT:
62 | payoff = np.array([-(max(K - S, 0) - option.price )for S in self.STs]) * number
63 |
64 | elif (type(instr) == Stock):
65 | stock = instr
66 | if side == BuySellSide.BUY:
67 | payoff = np.array([S - stock.price for S in self.STs]) * number
68 | elif side == BuySellSide.SELL:
69 | payoff = np.array([ stock.price -S for S in self.STs]) * number
70 | return payoff
71 |
72 | def plot(self, **params):
73 | plt.plot(self.STs, self.payoffs, **params)
74 | plt.title(f"Payoff Diagram for {self.name}")
75 | plt.fill_between(self.STs, self.payoffs,where=(self.payoffs > 0), facecolor='g', alpha=0.4)
76 | plt.fill_between(self.STs, self.payoffs,where=(self.payoffs < 0), facecolor='r', alpha=0.4)
77 | plt.xlabel(r'$S_T$')
78 | plt.ylabel('Profit in $')
79 | plt.show()
80 |
81 | def describe(self):
82 | max_profit = self.payoffs.max()
83 | max_loss = self.payoffs.min()
84 | print(f"Max Profit: ${round(max_profit, 3)}")
85 | print(f"Max loss: ${round(max_loss, 3)}")
86 | c = 0
87 | for o in self.instruments:
88 | print(o)
89 | if o.type == 'call' and o.side == 1:
90 | c += o.price
91 | elif o.type == 'call' and o.side == -1:
92 | c -= o.price
93 | elif o.type == 'put' and o.side == 1:
94 | c += o.price
95 | elif o.type == 'put' and o.side == -1:
96 | c - + o.price
97 |
98 | print(f"Cost of entering position ${c}")
99 |
100 | def compute_greek_profile(self , T, r, sigma ):
101 |
102 | instr_side =list(zip(self.side,self.instruments))
103 |
104 | for side , instr in instr_side:
105 | self.delta = self.delta + side * Greeks.Delta(self.STs ,instr.K ,T , r , sigma , instr.type)
106 | self.gamma = self.gamma + side * Greeks.Gamma(self.STs ,instr.K ,T , r , sigma)
107 | self.vega = self.vega + side * Greeks.Vega(self.STs ,instr.K ,T , r , sigma )
108 | self.theta = self.theta + side * Greeks.Theta(self.STs ,instr.K ,T , r , sigma , instr.type)
109 |
110 | def plotGreek(self,greekStr, **params):
111 | greek= getattr(self, greekStr)
112 | plt.plot(self.STs, greek, **params)
113 | plt.title(f"{greekStr} Profile")
114 | plt.xlabel(r'$S_T$')
115 | plt.show()
--------------------------------------------------------------------------------
/Option/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Option/__init__.py
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Option trading and analysis
2 |
3 | Designed for educational purposes. Here are some of the key topics:
4 |
5 |
6 | - **Data request** : Recovery of Option & stock financial data via the yFinance API
7 | - **Option Trading Strategies**: We'll explore various strategies for trading options, including both traditional and more exotics techniques.
8 | - **Black & Scholes Pricing Model**: Implementation of the theoretical foundations of the Black & Scholes model:Observation of the parameters affecting the value of an option.
9 | - **Implied Volatility Surface / Local Volatility**: Recovery and calculation of implied volatilities quoted on the market. Observing the long and short term skew/smile.Calculation of local volatility with the Dupire formula.
10 | - **Stochastic Volatility**: We'll explore the concept of stochastic volatility with Heston Model
11 | - **Greeks**: We'll cover the key "Greeks" (Delta, Gamma, Theta, Vega, Rho) and their profiles with different options strategies.
12 |
13 |
14 | ----------------------
15 |
16 |
17 |
18 | **Data Retrieval**
19 |
20 | The initial step in this code utilizes the yahoofinance API to access financial quotes. This is achieved through the use of the internal modules *y_finane_option_data* and *y_finane_stock_data*, which are utilized to retrieve option and stock data for a specific ticker symbol.
21 |
22 | During all the analysis of Read.me we will use as symbol *TSLA*
23 |
24 |
25 | Here is the simplest code to retrieve data:
26 |
27 | ```ruby
28 |
29 | from DataRequest import y_finane_option_data , y_finane_stock_data
30 |
31 | # Choose the instruments you want to recover
32 | ticker = 'TSLA'
33 |
34 | ## Get Option & underlying stock price
35 | option_df = y_finane_option_data.get_option_data(ticker)
36 | stockPrices_ = y_finane_stock_data.get_stock_price(ticker)
37 | ```
38 |
39 | Click here to access to dataFolder scprit [Data Request folder](https://github.com/AdrienCss/OptionTrading/blob/main/DataRequest)
40 |
41 | Example of output with TLSA:
42 |
43 | 
44 |
45 | # **Creating Option trading stategies**
46 |
47 | - [mainOptionStrategies.py](https://github.com/AdrienCss/OptionTrading/blob/main/mainOptionStrategies.py)<=
48 |
49 |
50 | In this script, we will construct a variety of strategies using real optional data.
51 | We will establish "Option"& "Stocks" data type and devise distinct strategies from it.
52 | Additionally, we will plot the corresponding Payoff profiles for these strategies. We will have the capability to display the Greek profiles of these strategies, projected over a range of underlying prices. This will allow us to analyze and evaluate the potential outcomes and risks associated with each strategies.
53 |
54 | In the case of the following strategies we have taken real options that quote the market on the underlying TSLA.
55 |
56 | Click here to see the code of the strategy Engine => [OptionStrategies.py](https://github.com/AdrienCss/OptionTrading/blob/main/Option/OptionStrategies.py)<=
57 |
58 | ```ruby
59 |
60 | # We take the 5th closest maturity
61 | maturity =option_df['T_days'].unique()[5]
62 |
63 | options_df = option_df[option_df['T_days'] ==maturity]
64 | call_df = options_df[options_df['Type'] =='CALL']
65 | put_df = options_df[options_df['Type'] =='PUT']
66 |
67 | #Getting real Quotes options
68 | call_90_df = call_df.iloc[(call_df['strike']-(currentprice * 0.90)).abs().argsort()[:1]]
69 | call_80_df = call_df.iloc[(call_df['strike']-(currentprice * 0.80)).abs().argsort()[:1]]
70 |
71 | put_90_df = put_df.iloc[(put_df['strike']-(currentprice * 0.90)).abs().argsort()[:1]]
72 | put_80_df = put_df.iloc[(put_df['strike']-(currentprice * 0.80)).abs().argsort()[:1]]
73 |
74 |
75 | # Creating Call spread 80 / 90
76 | call90 = Option(price=call_90_df['lastPrice'].values[0], K=call_90_df['strike'].values[0] , type= OpionType.CALL)
77 | call80 = Option(price=call_80_df['lastPrice'].values[0], K=call_80_df['strike'].values[0] , type= OpionType.CALL)
78 |
79 |
80 | strategy = OptionStrategies(name = "Call spread 90/80" ,St = currentprice)
81 | strategy.add_Option(option= call90 ,buySell= BuySellSide.SELL , option_number=1 )
82 | strategy.add_Option(option= call80 ,buySell= BuySellSide.BUY , option_number=1 )
83 | strategy.plot()
84 |
85 |
86 | # Creating put spread 80 / 90
87 | put90 = Option(price=put_90_df['lastPrice'].values[0], K=put_90_df['strike'].values[0] , type= OpionType.PUT)
88 | put80 = Option(price=put_80_df['lastPrice'].values[0], K=put_80_df['strike'].values[0] , type= OpionType.PUT)
89 |
90 |
91 | strategy = OptionStrategies(name = "PUT Spread 90/ 80" ,St = currentprice)
92 | strategy.add_Option(option= put90 ,buySell= BuySellSide.BUY , option_number=1 )
93 | strategy.add_Option(option= put80,buySell= BuySellSide.SELL , option_number=1 )
94 | strategy.plot()
95 | ```
96 |
97 |
98 | Put Spread Payoff 80%/90% | Call Spread Payoff 80%/90%
99 | :-------------------------:|:-------------------------:
100 |
|
101 |
102 |
103 |
104 | -Butterflies , Straddle , Strangle
105 |
106 |
107 |
108 | Straddle Payoff | Strangle Payoff
109 | :-------------------------:|:-------------------------:
110 |
|
111 |
112 |
113 | Long Butterfly spread | Short Butterfly spread
114 | :-------------------------:|:-------------------------:
115 |
|
116 |
117 |
118 |
119 |
120 | A synthetic call is a combination of a stock and a cash position, such as a long stock position and a short put option position, that simulates the payoff of a long call option. A synthetic put is a combination of a stock and a cash position, such as a short stock position and a long call option position, that simulates the payoff of a long put option. These options strategies can be used to replicate the payout of a call or put option, while potentially reducing the cost or risk associated with buying or selling the actual option.
121 |
122 | ```ruby
123 | #synthetic call
124 |
125 | put_df1 = put_df.iloc[(put_df['strike']-(currentprice - 15)).abs().argsort()[:1]]
126 |
127 | put = Option(price=put_df1['lastPrice'].values[0], K=put_df1['strike'].values[0] , type= OpionType.PUT)
128 | stock = Stock(price = last_price)
129 |
130 | strategy = OptionStrategies(name = "Synthetic call" ,St = currentprice)
131 | strategy.add_Option(option= put ,buySell= BuySellSide.BUY, option_number=1 )
132 | strategy.add_deltaOne(stock=stock,buySell= BuySellSide.BUY )
133 | strategy.plot()
134 |
135 |
136 |
137 | #synthetic PUT
138 |
139 | call_df1 = call_df.iloc[(call_df['strike']-(currentprice - 15)).abs().argsort()[:1]]
140 |
141 | call = Option(price=call_df1['lastPrice'].values[0], K=call_df1['strike'].values[0] , type= OpionType.CALL)
142 | stock = Stock(price = currentprice)
143 |
144 | strategy = OptionStrategies(name = "Synthetic PUT", St = currentprice)
145 | strategy.add_Option(option= call ,buySell= BuySellSide.BUY, option_number=1 )
146 | strategy.add_deltaOne(stock=stock,buySell= BuySellSide.SELL )
147 | strategy.plot()
148 | ```
149 |
150 |
151 |
152 | Synthetic call | Synthetic Put
153 | :-------------------------:|:-------------------------:
154 |
|
155 |
156 |
157 | -Covered call/ Put
158 |
159 | **Call Spread Greeks Profile**
160 | The engine also allows you to profile greek strategies.
161 | Here are the results obtained for a call spread
162 |
163 | ```ruby
164 | T = maturity /252
165 | r = 0.015
166 | vol = np.average(call_90_df['impliedVolatility'].values[0] + call_80_df['impliedVolatility'].values[0])
167 |
168 | strategy.compute_greek_profile(T ,r , vol)
169 | strategy.plotGreek(greekStr='gamma')
170 | strategy.plotGreek(greekStr='theta')
171 | strategy.plotGreek(greekStr='delta')
172 | strategy.plotGreek(greekStr='vega')
173 | ```
174 |
175 | Delta profile | Gamma Profile
176 | :-------------------------:|:-------------------------:
177 |
|
178 | Vega profile | Theta Profile
179 |
|
180 |
181 | # **Call/ Put Parity**
182 |
183 | - [mainCallPutParity.py](https://github.com/AdrienCss/OptionTrading/blob/main/mainPricingModel.py)
184 | -
185 | Call-Put Parity is a concept that refers to the relationship between call options and put options on the same underlying asset.
186 | The relationship is given by the following formula:
187 |
188 | $$
189 | C +K*\exp(-rT) = S_0 + P
190 | $$
191 |
192 | Through this simple relationship we will see if there are arbitrage opportunities in the market.
193 | First, let's observe for a chosen maturity the call and put prices on the market for a strike range
194 |
195 |
196 | As we can see, the market prices seem to resemble classic option prices, without any particular problem.
197 |
198 | Now let's calculate the corresponding prices of calls and puts to see if arbitrage opportunities can exist..
199 |
200 |
201 | Call | Put
202 | :-------------------------:|:-------------------------:
203 |
|
204 |
205 |
206 | In both cases the two curves are not distinctly observable!
207 | This proves that the market is totally efficient and that arbitrage opportunities do not exist on these options
208 |
209 | # **Option Princing : Binomial & B&S**
210 |
211 | - [mainPricingModel.py](https://github.com/AdrienCss/OptionTrading/blob/main/mainPricingModel.py)
212 |
213 | The B&S model is based on the following assumptions:
214 |
215 | - The underlying asset price follows a geometric Brownian motion with constant drift and volatility.
216 | - There are no transaction costs or taxes.
217 | - Trading can be done continuously.
218 | - The risk-free interest rate is constant.
219 | - The option can only be exercised at expiration.
220 |
221 | Under these assumptions, the Black-Scholes model provides a closed-form solution for the price of a European call or put option.
222 |
223 | The binomial model provides a multi-period view of the underlying asset price as well as the price of the option. In contrast to the Black-Scholes model, which provides a numerical result based on inputs, the binomial model allows for the calculation of the asset and the option for multiple periods along with the range of possible results for each period .
224 |
225 |
226 | ```ruby
227 |
228 | #Binomial Method
229 |
230 | def combos(n, i):
231 | return math.factorial(n) / (math.factorial(n - i) * math.factorial(i))
232 |
233 |
234 | def binom_EU1(S0, K, T, r, sigma, N, type_='call'):
235 | dt = T / N
236 | u = np.exp(sigma * np.sqrt(dt))
237 | d = np.exp(-sigma * np.sqrt(dt))
238 | p = (np.exp(r * dt) - d) / (u - d)
239 | value = 0
240 | for i in range(N + 1):
241 | node_prob = combos(N, i) * p ** i * (1 - p) ** (N - i)
242 | ST = S0 * (u) ** i * (d) ** (N - i)
243 | if type_ == 'CALL':
244 | value += max(ST - K, 0) * node_prob
245 | elif type_ == 'PUT':
246 | value += max(K - ST, 0) * node_prob
247 | else:
248 | raise ValueError("type_ must be 'call' or 'put'")
249 |
250 | return value * np.exp(-r * T)
251 | ```
252 |
253 |
254 |
255 | ```ruby
256 |
257 | #B&S Method
258 | def BS_CALL(S, K, T, r, sigma):
259 | d1 = (np.log(S/K) + (r + sigma**2/2)*T) / (sigma*np.sqrt(T))
260 | d2 = d1 - sigma * np.sqrt(T)
261 | return S * N(d1) - K * np.exp(-r*T)* N(d2)
262 |
263 |
264 | def BS_PUT(S, K, T, r, sigma):
265 | d1 = (np.log(S/K) + (r + sigma**2/2)*T) / (sigma*np.sqrt(T))
266 | d2 = d1 - sigma* np.sqrt(T)
267 | return K* np.exp(-r*T) * N(-d2) - S*N(-d1)
268 |
269 |
270 | def priceBS(S, K, T, r, sigma ,type='CALL'):
271 | if type == 'CALL':
272 | return BS_CALL(S, K, T, r, sigma)
273 | if type == 'PUT':
274 | return BS_PUT(S, K, T, r, sigma)
275 | else:
276 | raise ValueError('Unrecognized type')
277 |
278 | ```
279 |
280 |
281 | Volatility and time are two important factors that can significantly impact the price of a call option.
282 |
283 | -The higher the Implied volatility, the greater the likelihood that the option will expire in-the-money, and thus the higher the price of the call option. This is because with high volatility, there is a greater chance that the underlying asset's price will move above the strike price, making the option profitable.
284 |
285 | -Time, also known as time decay, is another important factor that can impact the price of a call option. As the expiration date of the option approaches, the option's value decreases. This is because there is less time for the underlying asset's price to move above the strike price and for the option to become profitable. The closer the expiration date, the greater the time decay, and the lower the price of the call option.
286 |
287 | Here is an example of how to observe the impact of volatility and Time on the price of an option:
288 |
289 | ```ruby
290 | #static Parameters
291 | import numpy as np
292 | from BlackAndScholes.BSPricing import BS_CALL ,BS_PUT
293 | import matplotlib.pyplot as plt
294 |
295 | K = 100
296 | r = 0.1
297 | sig = 0.2
298 | St = stockPrices_['Adj Close'].tail(1).values[0]
299 |
300 | S = np.arange(0 ,St + 60,1)
301 |
302 | #Using != Time
303 | callsPrice2 = [BS_CALL(s, K, 3, r, sig) for s in S]
304 | callsPrice3 = [BS_CALL(s, K,2, r, sig) for s in S]
305 | callsPrice4 = [BS_CALL(s, K, 1, r, sig) for s in S]
306 | callsPrice5 = [BS_CALL(s, K, 0.50, r,sig) for s in S]
307 |
308 | IntrinsicValue = [max(s - K ,0)for s in S]
309 |
310 | plt.plot(S, callsPrice2, label='Call Value T = 3')
311 | plt.plot(S, callsPrice3, label='Call Value T = 2')
312 | plt.plot(S, callsPrice4, label='Call Value T = 1')
313 | plt.plot(S, callsPrice5, label='Call Value T = 0.5')
314 | plt.plot(S, IntrinsicValue, label='IntrinsicValue')
315 | plt.xlabel('$S_t$')
316 | plt.ylabel(' Value')
317 | plt.title('Time impact on call value')
318 | plt.legend()
319 | plt.show()
320 |
321 |
322 | #Using != volatilities
323 | T= 1
324 | callsPrice2 = [BS_CALL(s, K, T, r, 0.2) for s in S]
325 | callsPrice3 = [BS_CALL(s, K, T, r, 0.3) for s in S]
326 | callsPrice4 = [BS_CALL(s, K, T, r, 0.4) for s in S]
327 | callsPrice5 = [BS_CALL(s, K, T, r, 0.5) for s in S]
328 |
329 |
330 | ```
331 |
332 |
333 | Implied Volatility | Time
334 | :-------------------------:|:-------------------------:
335 |
|
336 |
337 |
338 |
339 | # **Implied Volatility Calculation and Plotting for Options**
340 | - [mainImpliedVolatility.py](https://github.com/AdrienCss/OptionTrading/blob/main/mainImpliedVolatility.py)
341 |
342 | - Ploting observed implied volatility of real option's quotes.
343 | - Computing implied volatility using Newton-Raphson Model.
344 | - ploting skew/smile on short and long maturites ( short/long Smile)
345 |
346 |
347 |
348 | We can observe implied ( black) volatility Skew at different maturites ( long/short smile)
349 |
350 | **Volatility Skew for different maturities ( days)**
351 |
352 | ```ruby
353 | ticker ='TSLA'
354 |
355 | #Requesting stock price
356 | stockPrices_ = y_finane_stock_data.get_stock_price(ticker)
357 |
358 | #Compute daily's Log return
359 | stockPrices_['returns_1D'] = np.log(stockPrices_['Adj Close'] / stockPrices_['Adj Close'].shift(1))
360 | returns = stockPrices_['returns_1D'].dropna(axis=0)
361 |
362 | # details
363 | kurt = kurtosis(returns.values)
364 | sk = skew( returns.values)
365 |
366 | # Compute & plot volatility
367 | stockPrices_['realised_volatility_3M'] =stockPrices_['returns_1D'].rolling(60).std() * np.sqrt(252)
368 | stockPrices_['realised_volatility_6M'] =stockPrices_['returns_1D'].rolling(120).std()* np.sqrt(252)
369 |
370 | ```
371 |
372 | PUT Skew | CALL Skew
373 | :-------------------------:|:-------------------------:
374 |
|
375 |
376 |
377 | **Observation of Implied Volatility Surface**
378 |
379 | ```ruby
380 | import matplotlib.pyplot as plt
381 | from matplotlib import cm
382 |
383 | ## Get Option/underlying stock Prices
384 | option_df = y_finane_option_data.get_option_data(ticker)
385 | stockPrices_ = y_finane_stock_data.get_stock_price(ticker)
386 | last_price = stockPrices_.tail(1)['Adj Close'].values[0]
387 | option_df['underlying_LastPrice'] = last_price
388 |
389 | type = 'CALL'
390 | opt = option_df[(option_df.Type==type)]
391 | opt = opt[(opt.strike <= last_price+100)]
392 | opt = opt[(opt.strike >= last_price-100)]
393 | opt = opt[(opt.T_days <=200)]
394 | opt['MoneyNess'] = opt['underlying_LastPrice'] / opt['strike']
395 | opt = opt[['strike' , 'T_days','impliedVolatility']]
396 |
397 | # Initiate figure
398 | fig = plt.figure(figsize=(7, 7))
399 | axs = plt.axes(projection="3d")
400 | axs.plot_trisurf(opt.MoneyNess, opt.T_days , opt.impliedVolatility, cmap=cm.coolwarm)
401 | axs.view_init(40, 65)
402 | plt.xlabel("Strike")
403 | plt.ylabel("Days to expire")
404 | plt.title(f"Volatility Surface for {type} {ticker} - Implied Volatility as a Function of K and T")
405 | plt.show()
406 | ```
407 | Black Implied Volatility OTM | -
408 | :-------------------------:|:-------------------------:
409 |
| -
410 |
411 |
412 | We can observe it for each type of option also
413 |
414 | Black Implied Volatility CALL | Black Implied Volatility PUT
415 | :-------------------------:|:-------------------------:
416 |
|
417 |
418 |
419 |
420 |
421 | # **Local volatility vs implied Volatility**
422 |
423 |
424 | Steps to calculate locaL VOLATILITY
425 |
426 | -First, use the available quoted price to calculate the implied volatilities.
427 |
428 | -Appy interpolation method to produce a smooth implied volatility surface.
429 |
430 | -Plug implied volatilities into BSM model to get all the market prices of European calls.
431 |
432 | -Calculate the local volatility according to Dupire formula.
433 |
434 | $$ \begin{cases}
435 |
436 | \sqrt{\frac{DC}{DT} / \left(\frac{1}{2} * K^2 * \left(\frac{d^2C}{dK^2}\right)\right)}
437 |
438 | \end{cases}$$
439 |
440 |
441 | Local volatility surface :
442 |
443 |
444 |
445 |
446 | Local volatility vs Implied volatility observation
447 |
448 | Local Volatility | Obseratiob
449 | :-------------------------:|:-------------------------:
450 |
| Local volatility varies with market level about twice as rapidly as implied volatility varies with strike.
451 |
452 |
453 |
454 | # **Simulating heston Volatility**
455 |
456 | -[mainHestonSimulation.py](https://github.com/AdrienCss/OptionTrading/blob/main/mainHestonSimulation.py)
457 |
458 | The basic idea behind the Heston model is that the volatility of an asset's price is not constant over time, but rather follows a stochastic process. The model describes the dynamics of the asset's price and volatility using two state variables: the current price of the asset and its current volatility. The model then uses a set of parameters to describe how these state variables change over time.
459 |
460 |
461 | The Heston process is described by the system of SDEs:
462 |
463 | $$ \begin{cases}
464 | dS_t = \mu S_t dt + \sqrt{v_t} S_t dW^1_t \\
465 | dv_t = \kappa (\theta - v_t) dt + \sigma \sqrt{v_t} dW^2_t
466 | \end{cases}$$
467 |
468 | so we can use folowing discretionnary process:
469 |
470 | $$ \begin{cases}
471 | S_(t+1) =S_t + \mu S_t dt + \sqrt{v_t} S_t dW^1_t \\
472 | v_(t+1) =v_t + \kappa (\theta - v_t) dt + \sigma \sqrt{v_t} dW^2_t
473 | \end{cases}$$
474 |
475 |
476 | The parameters are:
477 | - $\mu$ drift of the stock process
478 | - $\kappa$ mean reversion coefficient of the variance process
479 | - $\theta$ long term mean of the variance process
480 | - $\sigma$ volatility coefficient of the variance process
481 | - $\rho$ correlation between $W^1$ and $W^2$ i.e. $dW^1_t dW^2_t = \rho dt$
482 |
483 | A few brief observations on the TSLA share price
484 |
485 | ```ruby
486 | ticker ='TSLA'
487 |
488 | #Requesting stock price
489 | stockPrices_ = y_finane_stock_data.get_stock_price(ticker)
490 |
491 | #Compute daily's Log return
492 | stockPrices_['returns_1D'] = np.log(stockPrices_['Adj Close'] / stockPrices_['Adj Close'].shift(1))
493 | returns = stockPrices_['returns_1D'].dropna(axis=0)
494 |
495 | # details
496 | kurt = kurtosis(returns.values)
497 | sk = skew( returns.values)
498 |
499 | # Compute & plot volatility
500 | stockPrices_['realised_volatility_3M'] =stockPrices_['returns_1D'].rolling(60).std() * np.sqrt(252)
501 | stockPrices_['realised_volatility_6M'] =stockPrices_['returns_1D'].rolling(120).std()* np.sqrt(252)
502 |
503 | ```
504 |
505 |
506 | Returns | Realized Volatility
507 | :-------------------------:|:-------------------------:
508 |
|
509 |
510 |
511 | The kurtosis of 5.08 indicates that the distribution of the data is higher than that of a normal distribution (kurtosis = 3), which means that there are more observations in the extreme values (highs and lows) than one would expect for a normal distribution. The skewness of -0.05 indicates that the distribution is slightly skewed to the left.
512 |
513 |
514 | In the case of the rolling volatility graph of TESLA stock returns, we can observe how the variability of returns changes over time. The average return to variance is 0.30.
515 |
516 |
517 |
518 | Result :
519 |
520 | Heston Volatility | Price trajectories (Monte Carlo)
521 | :-------------------------:|:-------------------------:
522 |
|
523 |
524 |
525 |
526 |
527 |
--------------------------------------------------------------------------------
/Volatility/DupireVolatility.py:
--------------------------------------------------------------------------------
1 | from BlackAndScholes import BSPricing
2 | import numpy as np
3 |
4 |
5 | from scipy.stats import norm
6 |
7 | N_prime = norm.pdf
8 |
9 |
10 | def ComputeDupireVolatility(S , K , r , T , sigma , type , increment = 1.5):
11 |
12 | BSp = BSPricing.priceBS
13 |
14 | delta_T_finite = (BSp(S=S , K=K , r=r, T=(T + increment), type=type, sigma=sigma) - BSp(S=S, K=K , r=r, T=T, type=type, sigma=sigma)) / (increment)
15 |
16 | delta_K_finite = (BSp(S=S , K=K + increment, r=r, T=T, type=type, sigma=sigma) - BSp(S=S, K=K , r=r, T=T, type=type, sigma=sigma)) / (increment)
17 |
18 | delta_2K_finite = (BSp(S=S , K=K + increment, r=r, T=T, type=type, sigma=sigma) - 2 * BSp(S=S, K=K , r=r, T=T, type=type, sigma=sigma)
19 | + BSp(S=S , K= K - increment, r=r, T=T, type=type, sigma=sigma)) / (increment)
20 |
21 | local_volatility = np.sqrt( (delta_T_finite + r * K * delta_K_finite) / (0.5 * K * K * delta_2K_finite))
22 |
23 | return local_volatility
24 |
25 |
26 | def ComputeDupireVolatilityQC(S , K , r , T , sigma , type , increment = 1.5):
27 |
28 | BSp = BSPricing.priceBS
29 |
30 | delta_T_finite = (BSp(S=S , K=K , r=r, T=(T + increment), type=type, sigma=sigma) - BSp(S=S, K=K , r=r, T=T, type=type, sigma=sigma)) / (2 *increment)
31 |
32 | delta_2K_finite = (BSp(S=S , K=K + increment, r=r, T=T, type=type, sigma=sigma) - 2 * BSp(S=S, K=K , r=r, T=T, type=type, sigma=sigma)
33 | + BSp(S=S , K= K - increment, r=r, T=T, type=type, sigma=sigma)) / (increment)**2
34 |
35 | local_volatility = np.sqrt( (delta_T_finite ) / (0.5 * K * K * delta_2K_finite))
36 |
37 | return local_volatility
38 |
39 |
40 | ##Check
41 |
42 | S=770.05
43 | K = 850
44 | sigma = 0.194722
45 | T =0.07
46 | increment = 1.5
47 |
48 | r = 0.0066
49 |
50 | localIV = ComputeDupireVolatility(S, K,r,T,sigma , 'CALL',increment)
--------------------------------------------------------------------------------
/Volatility/ImpliedVolatiliy.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 | import matplotlib.pyplot as plt
4 | from DataRequest import y_finane_option_data , y_finane_stock_data
5 | from BlackAndScholes.BSPricing import BS_PUT , BS_CALL
6 | from BlackAndScholes.Greeks import Vega
7 |
8 | from scipy.optimize import minimize_scalar
9 |
10 |
11 |
12 | def compute_theorical_IV(opt_value, S, K, T, r, type_='CALL' ):
13 | def call_obj(sigma):
14 | return abs(BS_CALL(S, K, T, r, sigma) - opt_value)
15 |
16 | def put_obj(sigma):
17 | return abs(BS_PUT(S, K, T, r, sigma) - opt_value)
18 |
19 | if type_ == 'CALL':
20 | res = minimize_scalar(call_obj, bounds=(0.01, 10), method='bounded')
21 | return res.x
22 | elif type_ == 'PUT':
23 | res = minimize_scalar(put_obj, bounds=(0.01, 10),
24 | method='bounded')
25 | return res.x
26 | else:
27 | raise ValueError("type_ must be 'put' or 'call'")
28 |
29 |
30 |
31 | def plot_ImpliedVolatility(option_df : pd.DataFrame , type,minDay , maxDay):
32 | df_optionsCall = option_df[option_df['Type'] == type]
33 | df_optionsCall = df_optionsCall[(option_df['T_days'] > minDay) & (option_df['T_days'] < maxDay)]
34 |
35 |
36 | grouped_df = df_optionsCall.groupby('T_days')
37 |
38 | fig, ax = plt.subplots()
39 |
40 | for key, group in grouped_df:
41 | ax.plot(group['strike'], group['impliedVolatility'], label=key)
42 |
43 | ax.legend()
44 | ax.set_xlabel('Strike')
45 | ax.set_ylabel('ImpliedVolatility')
46 | plt.title(f'{type} Options Volatility skew at different maturities (days) ')
47 | plt.show()
48 |
49 |
50 | def plot_ImpliedVolatilityCalculated(option_df : pd.DataFrame , type,minDay , maxDay):
51 | df_optionsCall = option_df[option_df['Type'] == type]
52 | df_optionsCall = df_optionsCall[(option_df['T_days'] > minDay) & (option_df['T_days'] < maxDay)]
53 |
54 |
55 | grouped_df = df_optionsCall.groupby('T_days')
56 |
57 | fig, ax = plt.subplots()
58 |
59 | for key, group in grouped_df:
60 | ax.plot(group['strike'], group['IV_Calculated'], label=key)
61 |
62 | ax.legend()
63 | ax.set_xlabel('Strike')
64 | ax.set_ylabel('ImpliedVolatility')
65 | plt.title(f'{type} Options Volatility skew at different maturities (days) ')
66 | plt.show()
67 |
68 |
69 | import numpy as np
70 | from scipy.stats import norm
71 |
72 | N_prime = norm.pdf
73 | N = norm.cdf
74 |
75 |
76 | def implied_volatility_Raphton(C, S, K, T, r, tol=0.0001,
77 | max_iterations=100):
78 | sigma = 0.8
79 |
80 | for i in range(max_iterations):
81 | diff = BS_CALL(S, K, T, r, sigma) - C
82 | if abs(diff) < tol:
83 | print(f'found on {i}th iteration')
84 | print(f'difference is equal to {diff}')
85 | break
86 |
87 | sigma = sigma - diff / Vega(S, K, T, r, sigma)
88 |
89 | return sigma
90 |
91 | def implied_volatility_put(P, S, K, T, r, tol=0.0001,
92 | max_iterations=100):
93 | sigma = 0.3
94 |
95 | for i in range(max_iterations):
96 | diff =BS_PUT(S, K, T, r, sigma) - P
97 | if abs(diff) < tol:
98 | print(f'found on {i}th iteration')
99 | print(f'difference is equal to {diff}')
100 | break
101 |
102 | sigma = sigma - diff / Vega(S, K, T, r, sigma)
103 |
104 | return sigma
--------------------------------------------------------------------------------
/Volatility/StochasticVolatility.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import pandas as pd
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 | import yfinance as yf
6 | import datetime
7 | import sys
8 |
9 | class VolData:
10 | def __init__(self, ticker):
11 | self.ticker = ticker
12 | p = yf.Ticker(self.ticker).info
13 | self.mid_price = (p['ask'] + p['bid'])/2
14 |
15 | """
16 | Initializing Class.
17 | Parameters
18 | ----------
19 | ticker: string
20 | The stock ticker of interest
21 | mid_price: integer
22 | Retrieves the mid-price of the stock which will be used in the class methods
23 | Example
24 | ---------
25 | >>> import yahoo_vol as vol
26 | >>> data = vol.Options(ticker="AAPL")
27 | """
28 |
29 | def get_input_data(self, save_csv=False):
30 | """
31 | Intermediate function used to collect the pricing data for plotting purposes.
32 | Parameter:
33 | ------------
34 | save_csv: boolean
35 | Select True to save the options data to a csv file
36 | Returns
37 | -------------
38 | dataframe:
39 | A dataframe with corresponding statistics for each option
40 | csv:
41 | A csv file will be saved if `save_csv` is set to True
42 | Example
43 | -------------
44 | df = data.get_input_data(save_csv=True)
45 | """
46 | storage = []
47 | x = yf.Ticker(self.ticker)
48 | option_dates = x.options
49 | if len(option_dates) == 0:
50 | sys.exit("Ticker does not have any options. Please try another ticker")
51 | for date in option_dates:
52 | try:
53 | call, put = x.option_chain(date)[0], x.option_chain(date)[1]
54 | call['option_type'], put['option_type'] = ('call', 'put')
55 | call['maturity'], put['maturity'] = (date, date)
56 | d = pd.concat([call, put])
57 | storage.append(d)
58 | except:
59 | print(date, "option maturity failed to download")
60 | pass
61 | print("All option dates successfully downloaded: ", len(storage) == len(option_dates))
62 | df = pd.concat(storage)
63 | df = df.reset_index()
64 | if save_csv==True:
65 | df.to_csv(str(self.ticker)+"_option_data.csv")
66 | return df
67 |
68 | def plot_vol_smile(self):
69 | """
70 | Plots the raw implied volatility against the respective strike values for all option
71 | maturities.
72 | Returns
73 | -------------
74 | plt.plot figure:
75 | Plots for each option maturity
76 | Example
77 | -------------
78 | data.plot_vol_smile()
79 | """
80 | data = self.get_input_data()
81 | # Extract and sort unique option maturities
82 | maturities = list(set(data.maturity))
83 | maturities.sort()
84 | # Loop through each maturity and plot the respective volatility smile
85 | for date in maturities:
86 | call = data[(data.maturity == date) & (data.option_type == 'call')]
87 | put = data[(data.maturity == date) & (data.option_type == 'put')]
88 | # Plotting Code
89 | plt.scatter(call.strike, call.impliedVolatility*100, c='g', label='Call Smile', alpha=0.50)
90 | plt.scatter(put.strike, put.impliedVolatility*100, c='r', label='Put Smile', alpha=0.50)
91 | plt.axvline(self.mid_price, linestyle='dashed', c='b', alpha=0.30, label='Mid Price (ATM)')
92 | plt.title(self.ticker.upper() + " Volatility Smile: " + date)
93 | plt.ylabel("Implied Volatility (%)")
94 | plt.xlabel("Strike")
95 | plt.legend()
96 | plt.show()
97 |
98 | def plot_vol_term_structure(self, n=3):
99 | """
100 | Plots the at-the-money (ATM) implied volatility for each respective option maturity to form
101 | a term structure.
102 | Parameter
103 | -------------
104 | n: integer
105 | "n" represents number of options above and below at-the-money (ATM) used to calculate the
106 | volatility term structure. For example, assuming the strikes are separated by $1, if n=3
107 | and ATM Price=100, then the following 6 strikes will be used for calculating the implied
108 | volatility term structure:
109 | - Lower ATM: 97, 98, 99
110 | - Upper ATM: 101, 102, 103
111 | Returns
112 | -------------
113 | plt.plot figure:
114 | Plot of implied volatility term structure
115 | Example
116 | -------------
117 | data.plot_term_structure(n=3)
118 | """
119 | data = self.get_input_data()
120 | strikes = list(set(data.strike))
121 | maturities = list(set(data.maturity))
122 | # Find the closest 'n' strikes to the current mid-price to approximate ATM option volatility
123 | get_closest_strike = min(range(len(strikes)), key=lambda i: abs(strikes[i]- self.mid_price))
124 | closest_ATM_indexes = list(range(get_closest_strike - (n-1),
125 | get_closest_strike + n))
126 | closest_ATM_strikes = [strikes[i] for i in closest_ATM_indexes]
127 | # Loop through all maturities to calculate the average implied volatility for each point
128 | storage = []
129 | for date in maturities:
130 | try:
131 | temp = data[(data.maturity == date)]
132 | storage.append(temp[temp['strike'].isin(closest_ATM_strikes)].impliedVolatility.mean())
133 | except:
134 | print(date, " failed to be included in the plot")
135 | pass
136 | df = pd.DataFrame([maturities, storage]).T
137 | df = df.sort_values(0)
138 | df = df.dropna()
139 | # Plotting Code
140 | plt.figure(figsize=(10,5))
141 | plt.plot(df[0], df[1]*100, marker='o')
142 | plt.gcf().autofmt_xdate()
143 | plt.xlabel("Date")
144 | plt.ylabel("Implied Volatility (%)")
145 | plt.title(str(self.ticker).upper() + " Volatility Term Structure")
146 | plt.tight_layout()
147 | plt.grid(True)
148 | plt.show()
--------------------------------------------------------------------------------
/Volatility/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AdrienCss/OptionTrading/27a6a88ddc827482beae7538756ad6a90f4fd238/Volatility/__init__.py
--------------------------------------------------------------------------------
/Volatility/hestonModel.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from DataRequest import y_finane_option_data , y_finane_stock_data
4 | import pandas as pd
5 |
6 |
7 |
8 | def heston_model_sim(S0, v0, rho, kappa, theta, sigma, T, r, M=10, ):
9 | N = 252
10 | dt = T / N
11 | mu = np.array([0, 0])
12 | cov = np.array([[1, rho], [rho, 1]])
13 |
14 | S = np.full(shape=(N + 1, M), fill_value=S0)
15 | v = np.full(shape=(N + 1, M), fill_value=v0)
16 |
17 | W = np.random.multivariate_normal(mu, cov, (N, M))
18 |
19 | for i in range(1, N + 1):
20 | S[i] = S[i - 1] * np.exp((r - 0.5 * v[i - 1]) * dt + np.sqrt(v[i - 1] * dt) * W[i - 1, :, 0])
21 | v[i] = np.maximum(v[i - 1] + kappa * (theta - v[i - 1]) * dt + sigma * np.sqrt(v[i - 1] * dt) * W[i - 1, :, 1] , 0)
22 |
23 | return S, v
24 |
25 |
26 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from Volatility import ImpliedVolatiliy
2 | from Option.OptionStrategies import OptionStrategies
3 | from Option.Option import Option , Stock
4 | from Enum.OptionType import OpionType
5 | from Enum.BuySellSide import BuySellSide
6 | from DataRequest import y_finane_option_data , y_finane_stock_data
7 | from Volatility.ImpliedVolatiliy import compute_theorical_IV, plot_ImpliedVolatility
8 |
9 | # Requesting data
10 | ticker = 'TSLA'
11 |
12 | ## Get Option/underlying stock Prices
13 | option_df = y_finane_option_data.get_option_data(ticker)
14 | stockPrices_ = y_finane_stock_data.get_stock_price(ticker)
15 |
16 | last_price = stockPrices_.tail(1)['Adj Close'].values[0]
17 | option_df['underlying_LastPrice'] = last_price
18 | currentRiskFreeRate = 0.015
19 |
20 | ## Get all Option at +/- 50 strike from underlyinng
21 | option_df = option_df[(option_df['strike'] < option_df['underlying_LastPrice'] + 50) & (option_df['strike'] > option_df['underlying_LastPrice'] - 50)]
22 |
23 |
24 | ## Compute & plot Implied Volatility
25 | option_df['calculated_IV'] = option_df.apply(lambda x: compute_theorical_IV(x['lastPrice'],
26 | x['underlying_LastPrice'],
27 | x['strike'],
28 | x['T_days'] / 365 ,
29 | currentRiskFreeRate,
30 | x['Type']), axis=1)
31 |
32 |
33 | ## Compute & plot Implied Volatility
34 | plot_ImpliedVolatility(option_df , 'CALL', 60 , 244)
35 | plot_ImpliedVolatility(option_df , 'PUT', 60 , 244)
36 |
37 |
38 | option_df = option_df[option_df['Type'] == "CALL"] # and df_options['T_days'] >1]
39 | option_df = option_df[option_df['T_days'] == 6] # and df_options['T_days'] >1]
40 |
41 | import matplotlib.pyplot as plt
42 | option_df.plot(x='strike', y=['impliedVolatility' ,'calculated_IV' ])
43 | plt.axvline(x =last_price , color = 'r', label = 'CurrentPrice')
44 | plt.title(f"{ticker}'s Option Implied volatility")
45 | plt.show()
46 |
47 |
48 |
49 | #Underlying pricen quote
50 | currentprice = last_price
51 | r=0.01
52 | vol = 0.05
53 | T =1
54 |
55 | callOption = Option(price= 3 , K=15 , type= OpionType.CALL)
56 | callOption2 = Option(price= 1 , K=25 , type= OpionType.CALL)
57 | #stock = Stock(price = currentprice)
58 |
59 | strategy = OptionStrategies(name = "Bear Spread" ,St = currentprice)
60 |
61 | strategy.add_Option(option= callOption ,buySell= BuySellSide.BUY , option_number=1 )
62 | strategy.add_Option(option= callOption2 ,buySell= BuySellSide.SELL , option_number=1 )
63 |
64 | strategy.compute_greek_profile(T ,r , vol)
65 |
66 | strategy.plot()
67 | strategy.plotGreek(greekStr='gamma')
68 | strategy.plotGreek(greekStr='theta')
69 | strategy.plotGreek(greekStr='delta')
70 | strategy.plotGreek(greekStr='vega')
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/mainCallPutParity.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | from DataRequest.y_finane_option_data import get_Option_Data_full , get_stock_price
3 | import matplotlib.pyplot as plt
4 |
5 | # the purpose of this script is to demonstrate de call/put parity
6 | # C + Ke(-rT) = P + S0
7 |
8 | ticker = 'TSLA'
9 | options_df = get_Option_Data_full(ticker)
10 |
11 | #Taking one maturity for observation
12 | maturities = options_df['T_days'].unique()
13 | maturity = maturities[3]
14 |
15 | options_df = options_df[options_df['T_days'] == maturity]
16 | options_df = options_df[['mid_price' , 'T_days' , 'strike','Type' , 'underlying_LastPrice']]
17 |
18 | call = options_df[options_df['Type'] =='CALL']
19 | put = options_df[options_df['Type'] =='PUT']
20 |
21 | df = pd.merge(call, put, how='inner', on ='strike')
22 | df = df.rename(columns={"mid_price_x": "calls_prices", "mid_price_y": "put_prices"})
23 |
24 | #plot
25 | df.plot(x='strike' , y =['calls_prices' , 'put_prices'])
26 | plt.title(f'Observed market prices for call and put options for {ticker} , T = {maturity}d')
27 | plt.show()
28 |
29 |
30 | df['P + S -K'] = df.put_prices + df.underlying_LastPrice_y - df.strike
31 |
32 | df.plot(x='strike' , y =['calls_prices' , 'P + S -K'])
33 | plt.title(f'Market calls prices vs calculated (C/P Parity) {ticker} , T = {maturity}d')
34 | plt.show()
35 |
36 |
37 | df['C + K -S'] = df.calls_prices + df.strike - df.underlying_LastPrice_y
38 | df.plot(x='strike' , y =['put_prices' , 'C + K -S'])
39 | plt.title(f'Market calls prices vs calculated (C/P Parity) {ticker} , T = {maturity}d')
40 | plt.show()
41 |
--------------------------------------------------------------------------------
/mainHestonSimulation.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from DataRequest import y_finane_option_data , y_finane_stock_data
4 | from scipy.stats import kurtosis, skew , norm,stats
5 | from Volatility.hestonModel import heston_model_sim
6 | from Volatility.ImpliedVolatiliy import compute_theorical_IV
7 | import pandas as pd
8 | import warnings
9 | warnings.simplefilter("ignore", category=FutureWarning)
10 |
11 |
12 | ticker ='TSLA'
13 |
14 | #Requesting stock price
15 | stockPrices_ = y_finane_stock_data.get_stock_price(ticker)
16 | optionPrice_ = y_finane_option_data.get_option_data(ticker)
17 |
18 | stockPrices_['returns_1D'] = np.log(stockPrices_['Adj Close'] / stockPrices_['Adj Close'].shift(1))
19 | returns = stockPrices_['returns_1D'].dropna(axis=0)
20 |
21 | # details
22 | kurt = kurtosis(returns.values)
23 | sk = skew( returns.values)
24 |
25 | # Plot chart
26 | plt.hist(returns, bins=100, density=True, color='blue', alpha=0.6)
27 | plt.text(0.6, 0.8, f'Kurtosis: {kurt:.2f}', transform=plt.gca().transAxes)
28 | plt.text(0.6, 0.7, f'Skewness: {sk:.2f}', transform=plt.gca().transAxes)
29 | plt.xlabel('Returns')
30 | plt.ylabel('Frequency')
31 | plt.title('Histogram of TSLA\'s Daily Returns')
32 | plt.show()
33 |
34 | # Compute & plot volatility
35 | stockPrices_['realised_volatility_3M'] =stockPrices_['returns_1D'].rolling(60).std() * np.sqrt(252)
36 | stockPrices_['realised_volatility_6M'] =stockPrices_['returns_1D'].rolling(120).std()* np.sqrt(252)
37 |
38 | stockPrices_.plot( y=['realised_volatility_3M','realised_volatility_6M'])
39 | plt.title(f'{ticker} stock Realized volatilities')
40 | plt.show()
41 |
42 | # Parameters
43 | timeSeries = stockPrices_.tail(300)
44 | currentPrice = stockPrices_.tail(1)['Adj Close'].values[0]
45 |
46 | timeSeries['variance_6M'] = timeSeries['realised_volatility_6M'] **2
47 |
48 | S0 = currentPrice # asset price
49 | r = 0.02 # risk-free rate
50 | T = 1.0 # time in years intil maturity , let's say 1year
51 |
52 | # Heston dependent parameters
53 | kappa = 3 #rate of mean reversion of variance under risk-neutral dynamics
54 | theta = timeSeries['realised_volatility_6M'].mean() **2 # long-term mean of variance under risk-neutral dynamics
55 | v0 = timeSeries['realised_volatility_6M'].tail(1).values[0] **2 # # initial variance under risk-neutral dynamics
56 | rho = 0.055604
57 | sigma = 0.6
58 |
59 |
60 | s_sim , vol_sim = heston_model_sim(S0, v0, rho, kappa, theta, sigma, T, r,10)
61 |
62 | timeSeries = timeSeries['Adj Close']
63 |
64 | startDate = timeSeries.tail(1).index.values
65 | startDate = startDate.astype(str)[0][:10]
66 |
67 | new_index = pd.date_range(start=startDate, end='2030-01-25', freq='B')
68 | s_sim = pd.DataFrame(s_sim)
69 | s_sim.index = new_index[:len(s_sim)]
70 | new_df = pd.concat([timeSeries,s_sim])
71 | new_df = new_df.fillna(method='ffill', axis=1)
72 |
73 |
74 | #Ploting volatilitty
75 | plt.plot(new_df.head(len(timeSeries)), color='blue')
76 | plt.plot(new_df.tail(len(s_sim)), color='red')
77 | plt.xticks(rotation=45)
78 | plt.ylabel('Price')
79 | plt.legend()
80 | plt.title(f'{ticker} Heston Price Paths simulation' )
81 | plt.show()
82 |
83 | #Ploting volatilitty
84 | vol_sim = pd.DataFrame(vol_sim)
85 | vol_sim.index = new_index[:len(vol_sim)]
86 | vol_sim.plot()
87 | plt.ylabel('Volatility')
88 | plt.title('Heston Stochatic Vol Simulation')
89 | plt.show()
90 |
91 |
--------------------------------------------------------------------------------
/mainImpliedVolatility.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from DataRequest import y_finane_option_data , y_finane_stock_data
3 | from Volatility.ImpliedVolatiliy import compute_theorical_IV , implied_volatility_Raphton
4 | import warnings
5 | import matplotlib.pyplot as plt
6 | from matplotlib import cm
7 | warnings.simplefilter("ignore", category=FutureWarning)
8 |
9 | # Requesting data
10 | ticker = 'TSLA'
11 |
12 | ## Get Option/underlying stock Prices
13 | option_df = y_finane_option_data.get_option_data(ticker)
14 | stockPrices_ = y_finane_stock_data.get_stock_price(ticker)
15 | last_price = stockPrices_.tail(1)['Adj Close'].values[0]
16 | option_df['underlying_LastPrice'] = last_price
17 | currentRiskFreeRate = 0.015
18 |
19 | ## Get all Option at +/- 50 strike from underlyinng
20 | option_df = option_df[(option_df['strike'] < option_df['underlying_LastPrice'] + 50) & (option_df['strike'] > option_df['underlying_LastPrice'] - 1000)]
21 | option_df['mid_Price'] = (option_df['bid'] + option_df['ask']) /2
22 |
23 | ## Compute Implied Volatility with 2 methdods
24 | IV = []
25 | for row in option_df.itertuples():
26 | price = compute_theorical_IV(row.mid_Price , row.underlying_LastPrice ,row.strike, row.T_days / 365, currentRiskFreeRate)
27 | IV.append(price)
28 |
29 | option_df['IV_Calculated_b'] = IV
30 |
31 |
32 | IV = []
33 | for row in option_df.itertuples():
34 | price = implied_volatility_Raphton(row.mid_Price , row.underlying_LastPrice ,row.strike, row.T_days / 365, currentRiskFreeRate )
35 | IV.append(price)
36 |
37 | option_df['IV_Calculated_n'] = IV
38 |
39 |
40 |
41 | # plot CALL Skew for each Maturities
42 | for matu in option_df.T_days.unique():
43 | exp = option_df[(option_df.T_days == matu) & (option_df.Type=='CALL')]
44 | plt.plot(exp.strike, exp.impliedVolatility, label='IV market')
45 | plt.plot(exp.strike, exp.IV_Calculated_b, label='IV_Calculated_b')
46 | plt.xlabel('Strike')
47 | plt.ylabel(f'Volatility')
48 | plt.title(f'Implied Volatility Skew ,for T = {matu}')
49 | plt.legend()
50 | plt.show()
51 |
52 |
53 | ## Get Option/underlying stock Prices
54 |
55 | plotSurface = False
56 |
57 | if plotSurface is True:
58 | opt = option_df[(option_df.inTheMoney==False)]
59 | opt = opt[(opt.strike <= last_price+100)]
60 | opt = opt[(opt.strike >= last_price-100)]
61 | opt = opt[(opt.T_days <=200)]
62 | opt = opt[['strike' , 'T_days','impliedVolatility']]
63 |
64 | # Initiate figure
65 | fig = plt.figure(figsize=(7, 7))
66 | axs = plt.axes(projection="3d")
67 | axs.plot_trisurf(opt.strike, opt.T_days , opt.impliedVolatility, cmap=cm.coolwarm)
68 | axs.view_init(40, 65)
69 | plt.xlabel("Strike")
70 | plt.ylabel("Days to expire")
71 | plt.title(f"Volatility Surface for OTM {ticker} - Implied Volatility as a Function of K and T")
72 | plt.show()
73 |
74 |
75 | ## Computing Dupire Volality for call Option
76 |
77 | from Volatility.DupireVolatility import ComputeDupireVolatility,ComputeDupireVolatilityQC
78 |
79 |
80 | Local_DupireIV = []
81 | for row in option_df.itertuples():
82 | localIV = ComputeDupireVolatility(row.underlying_LastPrice , row.strike , 0.0015 , row.T_days / 252,row.IV_Calculated_b ,row.Type,1.5)
83 | Local_DupireIV.append(localIV)
84 |
85 | option_df['Local_DupireIV'] = Local_DupireIV
86 |
87 |
88 | #Other formula of Dupire Volatility
89 |
90 | #Local_DupireIVQC = []
91 | #for row in option_df.itertuples():
92 | # localIV = ComputeDupireVolatilityQC(row.underlying_LastPrice , row.strike , 0.0015 , row.T_days / 252,row.IV_Calculated_b ,row.Type,1.5)
93 | # Local_DupireIVQC.append(localIV)
94 |
95 | #option_df['Local_DupireQC'] = Local_DupireIVQC
96 |
97 | matu = np.unique(option_df.T_days)
98 |
99 | for m in matu:
100 | opt = option_df[(option_df.Type=='CALL')]
101 | opt = opt[(opt.T_days ==m)]
102 | opt = opt[(opt.strike <= last_price+20)]
103 |
104 | plt.plot(opt.strike, opt.impliedVolatility, label='Black Implied volatility')
105 | plt.plot(opt.strike, opt.Local_DupireIV, label='Local volatility Dupire')
106 |
107 | plt.xlabel('Strike')
108 | plt.ylabel('volatility')
109 | plt.title(f'Black IV vs Dupire LV , T( days) ={m} ')
110 | plt.legend()
111 | plt.show()
112 |
113 |
114 | opt['ratio'] = opt.impliedVolatility /opt.Local_DupireIV
115 | plt.plot(opt.strike, opt.ratio, label="ratio")
116 | plt.xlabel('Strike')
117 | plt.ylabel('volatility')
118 | plt.title(f'ratio IV / LV')
119 | plt.legend()
120 | plt.show()
--------------------------------------------------------------------------------
/mainOptionStrategies.py:
--------------------------------------------------------------------------------
1 | from Option.OptionStrategies import OptionStrategies
2 | from Option.Option import Option , Stock
3 | from Enum.OptionType import OpionType
4 | from Enum.BuySellSide import BuySellSide
5 | from DataRequest import y_finane_option_data , y_finane_stock_data
6 | import numpy as np
7 |
8 | ticker = 'TSLA'
9 |
10 | ## Get Option/underlying stock Prices
11 | option_df = y_finane_option_data.get_option_data(ticker)
12 | stockPrices_ = y_finane_stock_data.get_stock_price(ticker)
13 | currentprice = stockPrices_.tail(1)['Adj Close'].values[0]
14 |
15 | #chose options
16 | # We take the 5th closest maturity
17 | maturity =option_df['T_days'].unique()[5]
18 |
19 | options_df = option_df[option_df['T_days'] ==maturity]
20 | call_df = options_df[options_df['Type'] =='CALL']
21 | put_df = options_df[options_df['Type'] =='PUT']
22 |
23 |
24 |
25 | #Getting real Quotes options
26 | call_90_df = call_df.iloc[(call_df['strike']-(currentprice * 0.90)).abs().argsort()[:1]]
27 | call_80_df = call_df.iloc[(call_df['strike']-(currentprice * 0.80)).abs().argsort()[:1]]
28 |
29 | put_90_df = put_df.iloc[(put_df['strike']-(currentprice * 0.90)).abs().argsort()[:1]]
30 | put_80_df = put_df.iloc[(put_df['strike']-(currentprice * 0.80)).abs().argsort()[:1]]
31 |
32 |
33 | # Creating Call spread 80 / 90
34 | call90 = Option(price=call_90_df['lastPrice'].values[0], K=call_90_df['strike'].values[0] , type= OpionType.CALL)
35 | call80 = Option(price=call_80_df['lastPrice'].values[0], K=call_80_df['strike'].values[0] , type= OpionType.CALL)
36 |
37 |
38 | strategy = OptionStrategies(name = "Call spread 90/80" ,St = currentprice)
39 | strategy.add_Option(option= call90 ,buySell= BuySellSide.SELL , option_number=1 )
40 | strategy.add_Option(option= call80 ,buySell= BuySellSide.BUY , option_number=1 )
41 | strategy.plot()
42 |
43 |
44 | # Creating put spread 80 / 90
45 | put90 = Option(price=put_90_df['lastPrice'].values[0], K=put_90_df['strike'].values[0] , type= OpionType.PUT)
46 | put80 = Option(price=put_80_df['lastPrice'].values[0], K=put_80_df['strike'].values[0] , type= OpionType.PUT)
47 |
48 |
49 | strategy = OptionStrategies(name = "PUT Spread 90/ 80" ,St = currentprice)
50 | strategy.add_Option(option= put90 ,buySell= BuySellSide.BUY , option_number=1 )
51 | strategy.add_Option(option= put80,buySell= BuySellSide.SELL , option_number=1 )
52 | strategy.plot()
53 |
54 |
55 | T = maturity /252
56 | r = 0.015
57 | vol = np.average(call_90_df['impliedVolatility'].values[0] + call_80_df['impliedVolatility'].values[0])
58 |
59 | strategy.compute_greek_profile(T ,r , vol)
60 | strategy.plotGreek(greekStr='gamma')
61 | strategy.plotGreek(greekStr='theta')
62 | strategy.plotGreek(greekStr='delta')
63 | strategy.plotGreek(greekStr='vega')
64 |
65 |
66 |
67 |
68 | # Creating Straddle
69 |
70 | call_dfs= call_df.iloc[(call_df['strike']-(currentprice - 15)).abs().argsort()[:1]]
71 | put_dfs = put_df[(put_df['strike'] == call_df['strike'].values[0])]
72 |
73 | put = Option(price=put_dfs['lastPrice'].values[0], K=put_dfs['strike'].values[0] , type= OpionType.PUT)
74 | call = Option(price=call_dfs['lastPrice'].values[0], K=call_dfs['strike'].values[0] , type= OpionType.CALL)
75 |
76 |
77 | strategy = OptionStrategies(name = "Straddle" ,St = currentprice)
78 | strategy.add_Option(option= put ,buySell= BuySellSide.BUY , option_number=1 )
79 | strategy.add_Option(option= call,buySell= BuySellSide.BUY , option_number=1 )
80 | strategy.plot()
81 |
82 |
83 | # Creating Strangle
84 |
85 | call_df= call_df.iloc[(call_df['strike']-(currentprice - 15)).abs().argsort()[:1]]
86 | put_df= put_df.iloc[(put_df['strike']-(currentprice - 70)).abs().argsort()[:1]]
87 |
88 |
89 | put = Option(price=put_df['lastPrice'].values[0], K=put_df['strike'].values[0] , type= OpionType.PUT)
90 | call = Option(price=call_df['lastPrice'].values[0], K=call_df['strike'].values[0] , type= OpionType.CALL)
91 |
92 | strategy = OptionStrategies(name = "Strangle" ,St = currentprice)
93 | strategy.add_Option(option= call ,buySell= BuySellSide.BUY, option_number=1 )
94 | strategy.add_Option(option= put ,buySell= BuySellSide.BUY, option_number=1 )
95 | strategy.plot()
96 |
97 |
98 | #synthetic call
99 |
100 | put_df1 = put_df.iloc[(put_df['strike']-(currentprice - 15)).abs().argsort()[:1]]
101 |
102 | put = Option(price=put_df1['lastPrice'].values[0], K=put_df1['strike'].values[0] , type= OpionType.PUT)
103 | stock = Stock(price = currentprice)
104 |
105 | strategy = OptionStrategies(name = "Synthetic call" ,St = currentprice)
106 | strategy.add_Option(option= put ,buySell= BuySellSide.BUY, option_number=1 )
107 | strategy.add_deltaOne(stock=stock,buySell= BuySellSide.BUY )
108 | strategy.plot()
109 |
110 |
111 | #synthetic PUT
112 |
113 | call_df1 = call_df.iloc[(call_df['strike']-(currentprice - 15)).abs().argsort()[:1]]
114 |
115 | call = Option(price=call_df1['lastPrice'].values[0], K=call_df1['strike'].values[0] , type= OpionType.CALL)
116 | stock = Stock(price = currentprice)
117 |
118 | strategy = OptionStrategies(name = "Synthetic PUT", St = currentprice)
119 | strategy.add_Option(option= call ,buySell= BuySellSide.BUY, option_number=1 )
120 | strategy.add_deltaOne(stock=stock,buySell= BuySellSide.SELL )
121 |
122 | strategy.plot()
123 |
124 | # Long Butterfly spread
125 |
126 | call1 = Option(price=8, K=currentprice - 25 , type = OpionType.CALL)
127 | call2 = Option(price=2, K=currentprice + 25, type = OpionType.CALL)
128 | call3 = Option(price=4, K=currentprice, type = OpionType.CALL)
129 |
130 |
131 | strategy = OptionStrategies('Butterfly Spread', St = currentprice )
132 | strategy.add_Option(option= call1 ,buySell= BuySellSide.BUY, option_number=1 )
133 | strategy.add_Option(option= call2 ,buySell= BuySellSide.BUY, option_number=1 )
134 | strategy.add_Option(option= call3 ,buySell= BuySellSide.SELL, option_number=2 )
135 | strategy.plot()
136 |
137 | # Short Butterfly spread
138 |
139 | strategy = OptionStrategies('Butterfly Spread', St = currentprice )
140 | strategy.add_Option(option= call1 ,buySell= BuySellSide.SELL, option_number=1 )
141 | strategy.add_Option(option= call2 ,buySell= BuySellSide.SELL, option_number=1 )
142 | strategy.add_Option(option= call3 ,buySell= BuySellSide.BUY, option_number=2 )
143 | strategy.plot()
144 |
--------------------------------------------------------------------------------
/mainPricingModel.py:
--------------------------------------------------------------------------------
1 | from BinomialModel.BinomialPricing import binom_EU1
2 | from BlackAndScholes.BSPricing import priceBS
3 | from DataRequest import y_finane_option_data , y_finane_stock_data
4 | from Volatility.ImpliedVolatiliy import compute_theorical_IV
5 |
6 | df = y_finane_option_data.get_option_data('TSLA')
7 | stockPrices_ = y_finane_stock_data.get_stock_price('TSLA')
8 |
9 | df['Underlying_Price'] = stockPrices_['Adj Close'].tail(1).values[0]
10 |
11 | prices_Bi = []
12 |
13 | for row in df.itertuples():
14 | price = binom_EU1(row.Underlying_Price, row.strike, row.T_days / 252, 0.01, 0.5, 200, row.Type)
15 | prices_Bi.append(price)
16 |
17 | df['Binomial_Prices'] = prices_Bi
18 | df['mid_Price'] = (df['bid'] + df['ask']) /2
19 |
20 | prices_BS = []
21 | for row in df.itertuples():
22 | price = priceBS(row.Underlying_Price, row.strike, row.T_days / 252, 0.01 ,row.impliedVolatility , row.Type)
23 | prices_BS.append(price)
24 |
25 |
26 | df['Black_And_Scholes_Prices'] = prices_BS
27 |
28 | IV = []
29 | for row in df.itertuples():
30 | price = compute_theorical_IV(row.mid_Price , row.Underlying_Price ,row.strike, row.T_days / 252, 0.01 , row.Type)
31 | IV.append(price)
32 | df['IV_Calculated'] = IV
33 |
34 |
35 |
36 | # Chose Expiration
37 | exp1 = df[(df.T_days == df.T_days.unique()[2]) & (df.Type=='CALL')]
38 |
39 | import matplotlib.pyplot as plt
40 | plt.plot(exp1.strike, exp1.mid_Price, label='Mid Price')
41 | plt.plot(exp1.strike, exp1.Black_And_Scholes_Prices, label='B&S Prices')
42 | plt.plot(exp1.strike, exp1.Binomial_Prices, label='Binomial Prices')
43 |
44 | plt.xlabel('Strike')
45 | plt.ylabel('Call Values')
46 | plt.legend()
47 | plt.show()
48 |
49 | # plotIV
50 | import matplotlib.pyplot as plt
51 | plt.plot(exp1.strike, exp1.impliedVolatility, label='IV market')
52 | plt.plot(exp1.strike, exp1.IV_Calculated, label='IV calculated')
53 |
54 | plt.xlabel('Strike')
55 | plt.ylabel('Implied Volatility Values')
56 | plt.legend()
57 | plt.show()
58 |
59 |
60 | #static Parameters
61 | import numpy as np
62 |
63 | K = 100
64 | r = 0.1
65 | T = 1
66 | St = stockPrices_['Adj Close'].tail(1).values[0]
67 |
68 | from BlackAndScholes.BSPricing import BS_CALL ,BS_PUT
69 | import matplotlib.pyplot as plt
70 |
71 | S = np.arange(0 ,St + 60,1)
72 |
73 | #Using != volatility
74 | callsPrice2 = [BS_CALL(s, K, T, r, 0.2) for s in S]
75 | callsPrice3 = [BS_CALL(s, K, T, r, 0.3) for s in S]
76 | callsPrice4 = [BS_CALL(s, K, T, r, 0.4) for s in S]
77 | callsPrice5 = [BS_CALL(s, K, T, r, 0.5) for s in S]
78 |
79 | IntrinsicValue = [max(s - K ,0)for s in S]
80 |
81 | plt.plot(S, callsPrice2, label='Call Value sig = 0.20')
82 | plt.plot(S, callsPrice3, label='Call Value sig = 0.30')
83 | plt.plot(S, callsPrice4, label='Call Value sig = 0.40')
84 | plt.plot(S, callsPrice5, label='Call Value sig = 0.50')
85 | plt.plot(S, IntrinsicValue, label='IntrinsicValue')
86 | plt.xlabel('$S_t$')
87 | plt.ylabel(' Value')
88 | plt.title('Implied volatility impact on call value')
89 | plt.legend()
90 | plt.show()
91 |
92 |
93 |
94 |
95 | #static Parameters
96 | import numpy as np
97 |
98 | K = 100
99 | r = 0.1
100 | T = 1
101 | sig = 0.2
102 | St = stockPrices_['Adj Close'].tail(1).values[0]
103 |
104 | from BlackAndScholes.BSPricing import BS_CALL ,BS_PUT
105 | import matplotlib.pyplot as plt
106 |
107 | S = np.arange(0 ,St + 60,1)
108 |
109 | #Using != volatility
110 | callsPrice2 = [BS_CALL(s, K, 3, r, sig) for s in S]
111 | callsPrice3 = [BS_CALL(s, K,2, r, sig) for s in S]
112 | callsPrice4 = [BS_CALL(s, K, 1, r, sig) for s in S]
113 | callsPrice5 = [BS_CALL(s, K, 0.50, r,sig) for s in S]
114 |
115 | IntrinsicValue = [max(s - K ,0)for s in S]
116 |
117 | plt.plot(S, callsPrice2, label='Call Value T = 3')
118 | plt.plot(S, callsPrice3, label='Call Value T = 2')
119 | plt.plot(S, callsPrice4, label='Call Value T = 1')
120 | plt.plot(S, callsPrice5, label='Call Value T = 0.5')
121 | plt.plot(S, IntrinsicValue, label='IntrinsicValue')
122 | plt.xlabel('$S_t$')
123 | plt.ylabel(' Value')
124 | plt.title('Time impact on call value')
125 | plt.legend()
126 | plt.show()
127 |
128 |
129 |
--------------------------------------------------------------------------------