Implied Volatility Skew¶

Open In Colab

Back out per-strike IV from live premiums via bisection.

Part 12 of 20 in the ServLoci algo/options trading notebook series — full index in notebooks/README.md.

Setup¶

# Get your dedicated static IPv6 + SOCKS5 credentials free:
#   https://comm.servloci.in/register        (or /auth/google?free=1 for an instant trial)
# Your api_key / api_secret pair shows up in the portal after signup:
#   https://comm.servloci.in/user
!pip install -q "requests[socks]"
!curl -sL https://comm.servloci.in/sdk/servloci.py -o servloci.py

import os
from servloci import ServLoci

SERVLOCI_API_KEY = os.environ.get("SERVLOCI_API_KEY", "dhan:1000000001")   # broker:client_id
SERVLOCI_API_SECRET = os.environ.get("SERVLOCI_API_SECRET", "")            # from the portal — leave blank to run this notebook in demo mode

sl = None
if SERVLOCI_API_SECRET:
    sl = ServLoci(api_key=SERVLOCI_API_KEY, api_secret=SERVLOCI_API_SECRET)
    print("ServLoci configured:", sl.host, sl.port)
else:
    print("SERVLOCI_API_SECRET not set — running in demo mode (no live proxy calls).")

Backs out implied volatility per strike via bisection against the Black-Scholes pricer (notebook 06), using live chain LTPs (notebook 11) — a rough call-side skew, not a full surface (single expiry assumption below).

from scipy.stats import norm
import math

def bs_price(opt_type, spot, strike, t_years, vol, rate=0.065):
    if t_years <= 0 or vol <= 0:
        return max(spot - strike, 0) if opt_type == "CE" else max(strike - spot, 0)
    d1 = (math.log(spot / strike) + (rate + vol * vol / 2) * t_years) / (vol * math.sqrt(t_years))
    d2 = d1 - vol * math.sqrt(t_years)
    if opt_type == "CE":
        return spot * norm.cdf(d1) - strike * math.exp(-rate * t_years) * norm.cdf(d2)
    return strike * math.exp(-rate * t_years) * norm.cdf(-d2) - spot * norm.cdf(-d1)

def implied_vol(opt_type, spot, strike, t_years, market_price, rate=0.065, lo=0.01, hi=3.0, iters=60):
    for _ in range(iters):
        mid = (lo + hi) / 2
        if bs_price(opt_type, spot, strike, t_years, mid, rate) > market_price:
            hi = mid
        else:
            lo = mid
    return (lo + hi) / 2

import requests

resp = requests.get("https://comm.servloci.in/api/market/option-chain", params={"symbol": "NIFTY"}, timeout=10)
data = resp.json()
spot, chain = data.get("spot", 24000), data.get("strikes", [])

t_years = 7 / 365  # placeholder — replace with actual days-to-expiry / 365
strikes, ivs = [], []
for row in chain:
    ltp = (row.get("ce") or {}).get("ltp")
    if isinstance(ltp, (int, float)) and ltp > 0:
        strikes.append(row["strike"])
        ivs.append(implied_vol("CE", spot, row["strike"], t_years, ltp) * 100)
import matplotlib.pyplot as plt

plt.plot(strikes, ivs, marker="o")
plt.axvline(spot, color="grey", linestyle="--", label="spot")
plt.title("NIFTY call IV skew (single-expiry proxy)")
plt.xlabel("Strike")
plt.ylabel("Implied vol (%)")
plt.legend()
plt.show()

« Previous: Live Option Chain Fetch
Next: Historical Data for Backtesting »

Try the concepts above interactively: Options Strategy Builder · Docs · Get your static IP