Welcome!
This is a Python project that allows you to visualize the implied volatility surface (IVs) for a chosen ticker, also you can customise the parameters used by the underlying model (Black-Scholes) to produce the surface.
Here is the list of all available commands:
- risk-free rate & dividend yield: choose respectively the risk free rate and the dividend yield to insert in the black scholes model.
- Min./Max. strike: choose the minimum and maximum strike price, in % from the spot price, to consider for the surface.
We know that trough the Black-Scholes model we can obtain the arbitrage-free price of an option, given the appropriate inputs. While parameters such as the risk-free rate, time to maturity, and the underlying asset's spot price are readily available, the current volatility of the asset is much harder to determine. A common approach is to use historical volatility, but there’s no guarantee that past fluctuations will persist throughout the option’s lifespan. To solve this problem, we turn to a market-driven measure: implied volatility (IV).
Options are actively traded, their prices reflect the collective expectations of market participants, just like any other traded asset. The idea is to input into the Black-Scholes formula all known parameters and trying many values of volaitlity until the model produces a price equal to the observed one in the market. This is a straightforward numerical problem: iterating over possible volatility values until the difference between theoretical price and observed price is minimised. The resulting value is the implied volatility.
Beyond its role in pricing exotic derivatives, IV offers valuable insights into market sentiment. A high IV suggests that traders anticipate large price swings and greater uncertainty, while a low IV indicates expectations of stability. Unlike the underlying asset price, which fluctuates constantly in liquid markets, IV is more stable over time, making it a useful metric for comparing assets and assessing market expectations.
When an asset has options written on it, we can obtain all possible expirations trough: ticker.options (assuming ticker = yf.Ticker('SPY')), the result is a list of possible expirations like '2025-02-20', '2025-02-21', '2025-02-24', '2025-02-25', ... this means that for every given available date we'll have many options we can trade at difference strike prices.
Let's take for example 2025-02-21, trough what is called option chain we cann see all available options that we can trade and that will expiry on this date. Obtaining the option chain for that date is straightforward: ticker.option_chain(expirations[1]).calls (considering just calls), this will produce:
As you see there are 181 calls that will expire on 2025-02-21, since we're considering SPY one may notice that some options are not being traded since many days, this is because not all options on a given maturity are liquid (low demand, low amount of transactions). That is because traders usually focus on near-the-money options with higher volume and tighter bid-ask spreads:
- in-the-money (ITM): this terms refers to all options, on a given maturity, where the stock price is higher than the strike price (vice-versa for put options). This kind of contract is said to have intrinsic value, basically it allows you to buy a certain stock for a price lower than the spot price (assuming the option stays ITM until expiration), hence the more ITM an option is (deep ITM), the more expensive it'll be to purchase it, hence the progressively low liquidity.
- out-the-money (OTM): as you may guess, this refers to options where the underlying stock price is lower than the strike price (vice-versa for put options). This is the other side of ITM, hence the more OTM an option goes (deep OTM) the less it'll be valued, meaning they have little intrisic value, hence the progressively low liquidity.
Hence, the most liquid options are ones that are slightly ITM or sligtly OTM (also called Near The Money), but most importantly when they are at-the-money (ATM). An option is ATM when the underlying price is equal to the strike price, those options does not have intrinsic value, but they have extrinsic or time value because they could end up being ITM before expiration. ATM options are specially used by traders to build spreads or other combinations.
Hence the moneyness on an option is defined as the ratio between the strike price and the spot price of the underlying asset (i.e. strike/spot).
In relation to this, a filter has been added (min./max. strike in % from option price) that allows you to filter the dataset to include only options that have strike prices that are a certain percentage above or below the current spot price.
Thank's for cheking out my project.

