The main topic of this article will be the visualization of payoffs and the pricing of option strategies (Straddle/Strangle/Spreads/etc..)
Hiram
Hiram is a free financial library built in Python that can be used for option pricing and financial instruments management.
Links
Table of Content
- Option creation and Pricing
- Call and Put Vizualization
- Straddle
- Strangle
- Strip
- Strap
- Bull Call Spread
- Put Call Parity Review
- Bear Put Spread
- Butterfly Spread
- Digital Replication
Introduction
I was wrong about my project architecture. My main goal was to make the code as concise and easy to use as possible.
I realized that this way was wrong as I tried to add new options/models.
So I gave up on my old python class
# Old
example_call = BlackScholes(way=Way_option.call, S=100, K=105, T=1, r=0.05, sigma=0.3)
This one allowed you to declare and price in a single line of code.
I’ve replaced it with the one below, which is more robust, more prod-oriented and more maintainable over time, as my project evolves.
# New
call = VanillaOption(style="euro", k=100, t=.2, way="call")
bsm = BlackScholesModel(spot=100, r=.05, sigma=.35)
call.pricer(model=bsm)
VanillaOption() only includes information related to the option you want to create.
VanillaOption(style="euro", k=100, t=.2, way="call")
BlackScholesModel() incorporates the market parameters at time t.
BlackScholesModel(spot=100, r=.05, sigma=.35)
Finally, calling the pricer() method returns and integrates the option’s pricing according to the model chosen inside the VanillaOption() object.
call.pricer(model=bsm)
print(call)
# Output
{'way': 'call',
'k': 100.0,
't': 0.2,
'qty': 1,
'price': 1,
'q': 0,
'style': 'euro',
'stock': None,
'id_': UUID('0f77159a-a547-4556-918c-8835ecfb0757'),
'pricing_data': {'spot': 100.0,
'strike': 100.0,
'maturity': 0.2,
'value': 5.834014051476267,
'sigma': 0.3,
'r': 0.05,
'delta': 0.5507736162363317,
'gamma': 0.029438711989724864,
'vega': 0.17663227193834918,
'theta': -15.737264608327669,
'rho': 7.899235341487547,
'way': 'call',
'model': 'BlackScholes',
'pricing_date': '2023-07-17 10:03:02'}}
Setup
# This setup helps us to import files from parent directory
import sys
import os
current = os.path.dirname(os.path.realpath("example2_StrategiesPlot.ipynb"))
parent = os.path.dirname(current)
sys.path.append(parent)
1. Option Creation and Pricing
from option import VanillaOption
from model import BlackScholesModel
call = VanillaOption(k=120, t=.5, style="euro", way="call")
bsm = BlackScholesModel(spot=100, r=.05, sigma=.35)
call.pricer(model=bsm)
# Output
{'spot': 100.0,
'strike': 120.0,
'maturity': 0.5,
'value': 4.241905955485716,
'sigma': 0.35,
'r': 0.05,
'delta': 0.29683509553802406,
'gamma': 0.014139967994435582,
'vega': 0.24744943990262272,
'theta': -9.970382657604757,
'rho': 17.497317695595758,
'way': 'call',
'model': 'BlackScholes',
'pricing_date': '2023-07-18 07:26:22'}
put = VanillaOption(k=120, t=.5, style="euro", way="put")
bsm = BlackScholesModel(spot=100, r=.05, sigma=.35)
put.pricer(model=bsm)
# Output
{'spot': 100.0,
'strike': 120.0,
'maturity': 0.5,
'value': 21.27909539888563,
'sigma': 0.35,
'r': 0.05,
'delta': -0.6784748164903085,
'gamma': 0.014139967994435582,
'vega': 0.24744943990262272,
'theta': -8.648285993273557,
'rho': -17.497317695595758,
'way': 'put',
'model': 'BlackScholes',
'pricing_date': '2023-07-18 07:26:23'}
2. Call and Put Visualization
In both introductory option pricing courses and interview preparation, I’ve always found that the hardest part is creating a pricing intuition.
I imagine that this intuition is developed as you use these products in a professional environment.
Nevertheless, I wanted to start this project as an aid to quickly pricing options, and visualizing how options and Greeks behave.
from plot import plot_payoff_vanilla
plot_payoff_vanilla(call)
plot_payoff_vanilla(put)
3. Straddle
A straddle is a neutral options strategy that involves simultaneously buying both a put option and a call option for the underlying security with the same strike price and the same expiration date.
A trader will profit from a long straddle when the price of the security rises or falls from the strike price by an amount more than the total cost of the premium paid. The profit potential is virtually unlimited, so long as the price of the underlying security moves very sharply.
from strategy import Strategy
straddle = Strategy(style="straddle", k=100, t=.2)
bsm = BlackScholesModel(spot=100, r=0.05, sigma=0.3)
straddle.pricer(model=bsm)
# Output
{'spot': 100.0,
'strike': 100.0,
'maturity': 0.2,
'value': 10.673011477869338,
'sigma': 0.3,
'r': 0.05,
'delta': 0.11149739872349529,
'gamma': 0.05887742397944973,
'vega': 0.35326454387669837,
'theta': -28.97794416820853,
'rho': 0.0,
'way': 'long',
'model': 'BlackScholes',
'pricing_date': '2023-07-18 07:26:53'}
With the goal in mind of being more business-oriented, I preferred to change the output format to a dictionary/json.
from plot import plot_payoff_straddle
plot_payoff_straddle(straddle)
If you expect the price of the underlying asset to fluctuate only slightly and/or volatility to fall. You can sell the straddle and earn the premium.
straddle.qty = -1
plot_payoff_straddle(straddle)
4. Strangle
The Strangle may be a good fit if you think the underlying will experience a large price movement in the near future but you are not sure about the direction.
strangle = Strategy(style="strangle", k=85, k2=110, t=.2)
bsm = BlackScholesModel(spot=100, r=0.05, sigma=0.3)
strangle.pricer(model=bsm)
# Output
{'spot': 100.0,
'strike': 85.0,
'maturity': 0.2,
'value': 2.8005984368016907,
'sigma': 0.3,
'r': 0.05,
'delta': 0.19476064934303597,
'gamma': 0.03720086834246779,
'vega': 0.2232052100548067,
'theta': -18.05165932804553,
'rho': 3.5923390491831757,
'way': 'long',
'model': 'BlackScholes',
'pricing_date': '2023-07-18 11:15:11'}
from plot import plot_payoff_strangle
plot_payoff_strangle(strangle)
You could still sell the Strangle if you dont forecast volatility in the future.
# Let's say you want to instead short the strangle
strangle.qty = -1
plot_payoff_strangle(strangle)
5. Strip
A strip is essentially a long straddle, but instead utilizes two puts and one call instead of one of each.
Strip may be considered as a market neutral bearish strategy. It pays off relatively more when the underlying asset declines than when it rises.
strip = Strategy(k=100, k2=110, t=.5, style="strip")
bsm = BlackScholesModel(spot=100, r=0.05, sigma=0.3)
strip.pricer(bsm)
# Output
{'spot': 100.0,
'strike': 100.0,
'maturity': 0.5,
'value': 23.966612291014094,
'sigma': 0.3,
'r': 0.05,
'delta': -0.2284494342456168,
'gamma': 0.0550221481945368,
'vega': 0.825332222918052,
'theta': -27.20793364151202,
'rho': -19.453267051904064,
'way': 'long',
'model': 'BlackScholes',
'pricing_date': '2023-07-18 07:29:25'}
from plot import plot_payoff_strip
plot_payoff_strip(strip)
Let’s say you want to instead short the strip
strip.qty = -1
plot_payoff_strip(strip)
6. Strap
A strap is an option combination that involves purchasing two at the money calls and one at the money put.
Strap may be considered as a market neutral bullish strategy. It pays off relatively more when the underlying asset rises than when it declines.
strap = Strategy(k=100, k2=110, t=.5, style="strap")
bsm = BlackScholesModel(spot=100, r=0.05, sigma=0.3)
strap.pricer(bsm)
{'spot': 100.0,
'strike': 100.0,
'maturity': 0.5,
'value': 26.435621088180824,
'sigma': 0.3,
'r': 0.05,
'delta': 0.7468604777827158,
'gamma': 0.0550221481945368,
'vega': 0.825332222918052,
'theta': -29.675752769374895,
'rho': 19.453267051904064,
'way': 'long',
'model': 'BlackScholes',
'pricing_date': '2023-07-17 22:30:26'}
from plot import plot_payoff_strap
plot_payoff_strap(strap)
Let’s say you want to instead short the Strap
strap.qty = -1
plot_payoff_strap(strap)
7. Bull Call Spread
Bull spread may be the good strategy if you have a bullish view on the market. Bull Spread is designed to profit from a moderate rise in the price of the underlying security.
You can also model the bull spread with puts. I will add this feature in the future.
bull_spread = Strategy(k=80, k2=120, t=.5, style="bull_spread",qty=1, way="call")
bsm = BlackScholesModel(spot=100, r=0.05, sigma=0.3)
bull_spread.pricer(model=bsm)
#Output
{'spot': 100.0,
'strike': 80.0,
'maturity': 0.5,
'value': 26.13327748374865,
'sigma': 0.3,
'r': 0.05,
'delta': 0.6207439807990514,
'gamma': -0.007033223116091418,
'vega': -0.10549834674137129,
'theta': -1.336009490916319,
'rho': 25.138651886972593,
'way': 'call',
'model': 'BlackScholes',
'pricing_date': '2023-07-17 22:37:36'}
from plot import plot_payoff_bull_spread
plot_payoff_bull_spread(bull_spread)
8. Put Call Parity Review
It’s crucial to understand what’s the put call-parity. I’ve been asked about this several times in interviews.
The put–call parity defines a relationship between the price of a European call and European put, both with the identical strike price and expiry, namely that a portfolio of a long call option and a short put option is equivalent to a single forward contract at this strike price and expiry.
Put Call Parity Formula
Why this formula is crucial ?
The put-call parity theory is important to understand because this relationship must hold in theory. With European put and calls, if this relationship does not hold, then that leaves an opportunity for arbitrage.
Rearranging this formula, we can solve for any of the components of the equation. This allows us to create a synthetic call or put option.
If a portfolio of the synthetic option costs less than the actual option, based on put-call parity, a trader could employ an arbitrage strategy to profit.
9. Bear Put Spread
A Bear Spread is a bearish, vertical spread options strategy that can be used when the options trader is moderately bearish on the underlying security.
Because of put–call parity, a bear spread can be constructed using either put options or call options. If constructed using calls, it is a bear call spread (alternatively call credit spread). If constructed using puts, it is a bear put spread (alternatively put debit spread).
bear_spread = Strategy(k=80, k2=120, t=.5, style="bear_spread",qty=1, way="call")
bsm = BlackScholesModel(spot=100, r=0.05, sigma=0.3)
bear_spread.pricer(model=bsm)
# Ouput
{'spot': 100.0,
'strike': 80.0,
'maturity': 0.5,
'value': 26.13327748374865,
'sigma': 0.3,
'r': 0.05,
'delta': -0.6207439807990514,
'gamma': 0.007033223116091418,
'vega': 0.10549834674137129,
'theta': -7.665910295398596,
'rho': 25.138651886972593,
'way': 'call',
'model': 'BlackScholes',
'pricing_date': '2023-07-18 07:51:54'}
from plot import plot_payoff_bear_spread
plot_payoff_bear_spread(bear_spread)
10. Butterfly Spread
A butterfly is a limited risk, non-directional options strategy that is designed to have a high probability of earning a limited profit when the future volatility of the underlying asset is expected to be lower (when long the butterfly) or higher (when short the butterfly) than that asset’s current implied volatility.
butterfly_spread = Strategy(k=80, k2=100, k3=120, t=.5, style="butterfly_spread",qty=1)
bsm = BlackScholesModel(spot=100, r=0.05, sigma=0.3)
butterfly_spread.pricer(model=bsm)
# Output
{'spot': 100.0,
'strike': 80.0,
'maturity': 0.5,
'value': 45.40303074064702,
'sigma': 0.3,
'r': 0.05,
'delta': -0.015266988082805966,
'gamma': -0.012980483837469445,
'vega': -0.1947072575620417,
'theta': -3.582145639427016,
'rho': 64.04518599078072,
'way': 'long',
'model': 'BlackScholes',
'pricing_date': '2023-07-18 08:03:27'}
from plot import plot_payoff_butterfly_spread
plot_payoff_butterfly_spread(butterfly_spread)
# Let's say you want to instead be long vol
butterfly_spread.qty = -1
plot_payoff_butterfly_spread(butterfly_spread)
11. Digital Replication
I’ve had this question several times during interviews. The code below has nothing to do with pricing, but rather replication.
It gives a better understanding of how digital/binary works.
The Digital or Binary option is an exotic option in which the payoff is either some fixed monetary amount or nothing at all.
The two main types of binary options are the cash-or-nothing binary option and the asset-or-nothing binary option.
We’ll focus only on the cash-or nothing digital/binary
It provides traders with the opportunity of a fixed payout when the market price of the underlying asset exceeds the strike price.
Digital options offer traders with two possible outcomes of any trade – traders earn a profit if their predictions are correct, else they must lose their initial output.
Digital/binary payoff plot
As you can see above, as soon as the underlying reaches the strike 100. The premium turns from 0 to 1. Now how do you replicate this payoff? If you remember previous option strategies, this payoff might suggest the bull spread.
We can replicate the digital with two call options with different strikes. When we make the delta between those two strikes infinitely small and assume we have arbitrary strike prices. We get the digital
If we want to increase the premium received, we can buy more calls/bull spreads. See the graphs below
call1 = VanillaOption(style="euro", k=100, t=.2, way="call")
call2 = VanillaOption(style="euro", k=120, t=.2, way="call", qty=-1)
bsm = BlackScholesModel(spot=90, r=0.05, sigma=0.3)
call1.pricer(model=bsm)
call2.pricer(model=bsm)
The code above will return only the pricing data of the call2, in Jupyter Notebooks, only the last return is print.
# Ouput
{'spot': 90.0,
'strike': 120.0,
'maturity': 0.2,
'value': 0.09735246926622976,
'sigma': 0.3,
'r': 0.05,
'delta': -0.022383128703362808,
'gamma': -0.0044478440742041015,
'vega': -0.02161652220063193,
'theta': 1.5243704155205653,
'rho': 0.9667202615549277,
'way': 'call',
'model': 'BlackScholes',
'pricing_date': '2023-07-18 08:24:01'}
from plot import plot_payoff_digital_replication
plot_payoff_digital_replication(call1, call2,1)
Now let’s see the graphs of as we change the strike.
call2.k = 130
plot_payoff_digital_replication(call1, call2, 1)
call2.k = 115
plot_payoff_digital_replication(call1, call2, 1)
call2.k = 100.00001
plot_payoff_digital_replication(call1, call2, 1)
# let's make the qty bigger
plot_payoff_digital_replication(call1, call2, 5)
As you can see, as epsilon is reduced (the spread between strike and spot). The call spread tends to become a digital
If you want to increase the premium, increase the number of call-spreads. Here I’ve changed the quantity from 1 to 5
Thank you for reading this post. Don’t hesitate if you’d like that I add new features to my library.