{"id":6860,"date":"2026-09-04T10:48:40","date_gmt":"2026-09-04T10:48:40","guid":{"rendered":"https:\/\/eodhd.com\/financial-academy\/?p=6860"},"modified":"2026-09-04T11:02:31","modified_gmt":"2026-09-04T11:02:31","slug":"build-your-own-volatility-index-python","status":"publish","type":"post","link":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python","title":{"rendered":"How to Build a Historical Implied Volatility Series for Any US Stock (Python)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Here is a question that sounds simple and turns out to be surprisingly hard to answer: are options on Apple expensive right now?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Your broker will happily show you today&#8217;s implied volatility. Fine \u2014 but 25% implied volatility means nothing on its own. Is that high for Apple? Low? Perfectly ordinary for a Tuesday in September? Without a history to compare against, that number is just a number.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The obvious answer is &#8220;look at the VIX&#8221;, and the obvious answer is wrong. The VIX measures 30-day implied volatility on the S&amp;P 500. It tells you what the market thinks about the market. It says nothing about how nervous traders are about Apple specifically, and even less about a mid-cap you happen to be looking at. Single-stock volatility indices do exist \u2014 CBOE has published them for a handful of large caps over the years \u2014 but not for the ticker you care about, not reliably, and not as a series you can pull on demand.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So let&#8217;s build one. In this article I&#8217;ll construct a historical implied volatility series for a single US stock \u2014 daily, at the money, 30 days to expiry \u2014 out of end-of-day options data. The whole thing runs on the free demo token, and by the end you&#8217;ll be able to say things like &#8220;Apple&#8217;s 30-day IV is at the 47th percentile of the past year&#8221;, which is an actual answer to an actual question.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two things will get in the way. Both are worth knowing about before you write your own version, and neither is obvious from the documentation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All figures below are as of 1 September 2026, the last day in my sample. Rerun the code and you&#8217;ll get your own.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">What a historical implied volatility series actually is<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every listed option has its own implied volatility, and on any given day Apple has thousands of listed options. A strike far out of the money expiring next week and a strike near the money expiring in two years have wildly different IVs, and averaging them together gives you mush.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So we pin down two things and hold them fixed:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>At the money.<\/strong> The strike closest to where the stock is actually trading. This is where the most liquid, most informative options live.<\/li>\n<li><strong>Roughly 30 days to expiry.<\/strong> Same horizon the VIX uses, and short enough to be sensitive to news without being pure expiry-week noise.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Fix those, take one reading per trading day, and you get a clean time series: the market&#8217;s 30-day forecast for how much this one stock is going to move, tracked day by day.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One honest caveat before we start, because I&#8217;d rather say it up front than bury it at the bottom. This is a <em>proxy<\/em>, not a reimplementation of the CBOE VIX methodology. The real thing interpolates across two expiries to hit exactly 30 days and integrates across the whole strike chain. We&#8217;re picking the nearest listed contract to the money and accepting an expiry somewhere in a 25-to-35-day window. For the question &#8220;is vol high or low for this name&#8221;, that&#8217;s plenty. For pricing a variance swap, it isn&#8217;t. Know which one you&#8217;re doing.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">The fields that save you from Black-Scholes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;ve built something like this before against a raw options feed, you know the drill: fetch prices, fetch the risk-free rate, fetch dividends, then solve Black-Scholes backwards for each contract to recover implied volatility. It&#8217;s a lot of machinery, and every piece of it is a place to introduce a subtle bug.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/eodhd.com\/marketplace\/unicornbay\/options\">US Stock Options Data API<\/a> ships implied volatility as a field, already computed by the data provider, on every contract row. That&#8217;s a trade-off worth naming: you inherit somebody else&#8217;s model assumptions about dividends and early exercise instead of choosing your own. For measuring how volatility moves over time it&#8217;s the right trade \u2014 the assumptions stay constant across the series, so the shape you&#8217;re studying is real even if the absolute level would shift slightly under a different model.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two more fields quietly do most of our work:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>dte<\/strong> \u2014 days to expiry, as of that row&#8217;s date. No date arithmetic, no holiday calendars.<\/li>\n<li><strong>moneyness<\/strong> \u2014 distance from the money. Magnitude is roughly the gap between strike and spot as a fraction of spot, and the sign tells you whether the contract is in or out of the money, not whether the strike is above or below the price. So a deep in-the-money put and a deep out-of-the-money call both sit far from zero, in opposite directions. We only ever use it to rank candidates by distance from the money, so we take the absolute value and the sign convention stops mattering.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Those two turn &#8220;find the at-the-money contract about 30 days out&#8221; from a research problem into a sort. Let&#8217;s confirm the shape of the data with one request \u2014 a slice of a single contract&#8217;s life, one row per trading day:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">import requests\n\nBASE = \"https:\/\/eodhd.com\/api\/mp\/unicornbay\/options\"\nTOKEN = \"demo\"   # the demo token works for AAPL and AMZN, no registration\n\nr = requests.get(f\"{BASE}\/eod\", params={\n    \"filter[contract]\": \"AAPL270115C00260000\",\n    \"fields[options-eod]\": \"contract,strike,type,dte,moneyness,volatility\",\n    \"page[limit]\": 5,\n    \"api_token\": TOKEN,\n})\n\nfor row in r.json()[\"data\"]:\n    a = row[\"attributes\"]\n    print(row[\"id\"], a[\"dte\"], a[\"moneyness\"], a[\"volatility\"])<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p class=\"wp-block-paragraph\">Note the record id: it&#8217;s the contract name plus the date, like AAPL270115C00260000-2026-08-31. One row is one contract on one trading day. That detail is about to matter a lot.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Why the obvious approach cannot work<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The natural first instinct is to grab a whole expiry and sort through it locally. Let&#8217;s actually measure that instead of guessing. Apple&#8217;s January 2026 expiry, every strike, every date, walking the pagination to the end:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">params = {\n    \"filter[underlying_symbol]\": \"AAPL\",\n    \"filter[exp_date_eq]\": \"2026-01-16\",\n    \"fields[options-eod]\": \"contract,strike,type,dte\",\n    \"page[limit]\": 1000,\n    \"api_token\": TOKEN,\n}\n\nurl, pages, rows = f\"{BASE}\/eod\", 0, []\nwhile url:\n    payload = requests.get(url, params=params).json()\n    if \"data\" not in payload:            # we will hit this \u2014 see below\n        print(\"stopped:\", payload)\n        break\n    pages += 1\n    rows += payload[\"data\"]\n    url = payload.get(\"links\", {}).get(\"next\")\n    params = {\"api_token\": TOKEN} if url else None\n\nprint(f\"{len(rows)} rows over {pages} pages\")<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p class=\"wp-block-paragraph\">This does not finish. It collects 11,000 rows across 11 pages and then stops with:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">stopped: {'errors': {'page.offset': ['The page.offset may not be greater than 10000.']}}<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p class=\"wp-block-paragraph\">Pagination allows 1,000 records per request and caps the offset at 10,000, so a single query can reach about 11,000 rows and no further. And that one expiry is much bigger than that. Sweeping it in strike bands to count it properly, it comes to <strong>62,978 rows \u2014 96 strikes across 585 trading dates, calls and puts<\/strong>. Sixty-three pages of data behind an eleven-page door.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Five hundred and eighty-five dates for a single expiry surprised me too, until it didn&#8217;t: those contracts were listed nearly two years before they expired, and the endpoint returns every day of their life. There is no filter for the row&#8217;s own date \u2014 the tradetime filters match the contract&#8217;s last market activity, not the date of the record \u2014 so asking for one expiry always means asking for its entire history.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So brute force isn&#8217;t merely wasteful here, it&#8217;s impossible. What makes the job tractable is narrowing the strike range before you ask, using the one thing you already know: where the stock was trading.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s what band width actually buys you, measured on that same expiry:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Strike band around spot<\/th><th>Pages<\/th><th>Rows<\/th><th>Strikes<\/th><th>Usable dates in the 25\u201335 day window<\/th><\/tr><\/thead><tbody>\n<tr><td>\u00b11%<\/td><td>2<\/td><td>1,208<\/td><td>2<\/td><td>7<\/td><\/tr>\n<tr><td>\u00b12%<\/td><td>3<\/td><td>2,416<\/td><td>4<\/td><td>7<\/td><\/tr>\n<tr><td>\u00b13%<\/td><td>4<\/td><td>3,624<\/td><td>6<\/td><td>7<\/td><\/tr>\n<tr><td>\u00b16%<\/td><td>9<\/td><td>8,418<\/td><td>13<\/td><td>7<\/td><\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Every band yields the same seven usable dates \u2014 the wider ones just cost more. Tempting to take the cheapest row and move on. I tried that: a \u00b13% band over the full year still produced 253 daily observations from only 75 requests, which looks like a clear win \u2014 until you compare the output. On 28 of those 253 days the stock had drifted far enough from where it was when I picked the band that the genuinely at-the-money strike fell outside it, and the series quietly reported the second-best contract instead. The year&#8217;s volatility peak came out as 31.4% instead of 33.6%. Cheaper, and wrong exactly where it mattered.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So: \u00b16%. Nine pages per expiry instead of two, and no silent substitutions.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">The second gotcha: next doesn&#8217;t carry your token<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ll notice the pagination loop above puts the token back on every follow-up request. That isn&#8217;t decoration. The next link keeps your filters, your sort and your offset \u2014 but not your api_token. Follow it verbatim and you get:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">401 Unauthenticated<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p class=\"wp-block-paragraph\">Which is a genuinely nasty way to learn the lesson, because page one works perfectly and the failure only shows up on page two. If you&#8217;ve ever had a paging loop die exactly one page in, this is a good first thing to check.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Building the series<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">First the underlying&#8217;s price history \u2014 one request for the whole window, which is what lets us narrow the strike band later:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">from datetime import date, timedelta\nfrom collections import defaultdict\n\nSYMBOL = \"AAPL\"\nSTART, END = date(2025, 9, 1), date(2026, 9, 1)\n\npx = requests.get(f\"https:\/\/eodhd.com\/api\/eod\/{SYMBOL}.US\", params={\n    \"from\": \"2025-08-01\", \"period\": \"d\", \"fmt\": \"json\", \"api_token\": TOKEN,\n}).json()\n\nclose = {row[\"date\"]: row[\"adjusted_close\"] for row in px}\n\ndef price_near(day):\n    \"\"\"Closest available close on or before `day` \u2014 handles weekends and holidays.\"\"\"\n    for back in range(8):\n        key = (day - timedelta(days=back)).isoformat()\n        if key in close:\n            return close[key]\n    return None<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p class=\"wp-block-paragraph\">Then one function to pull an expiry inside a strike band, paginating properly and re-attaching the token:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">REQUESTS = 0\n\ndef fetch_expiry(expiry, lo, hi):\n    \"\"\"Every row for one expiry inside a strike band, following pagination.\"\"\"\n    global REQUESTS\n    params = {\n        \"filter[underlying_symbol]\": SYMBOL,\n        \"filter[exp_date_eq]\": expiry.isoformat(),\n        \"filter[strike_from]\": lo, \"filter[strike_to]\": hi,\n        \"fields[options-eod]\": \"contract,strike,type,dte,moneyness,volatility\",\n        \"page[limit]\": 1000, \"api_token\": TOKEN,\n    }\n    url, rows = f\"{BASE}\/eod\", []\n    while url:\n        payload = requests.get(url, params=params).json()\n        REQUESTS += 1\n        rows += payload.get(\"data\", [])\n        url = payload.get(\"links\", {}).get(\"next\")\n        params = {\"api_token\": TOKEN} if url else None\n    return rows<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p class=\"wp-block-paragraph\">Now the loop. US equity options overwhelmingly expire on Fridays, so rather than spending requests to enumerate expiries we generate every Friday in the window and let empty responses tell us which ones don&#8217;t exist. It isn&#8217;t a perfect rule \u2014 of the 89 past Apple expiries I pulled, two landed on a Thursday (27 March 2024 and 17 April 2025, both ahead of a holiday), and heavily traded ETFs like SPY also list Monday and Wednesday weeklies. For a single name it costs you the odd expiry; if you extend this to SPY, enumerate expiries properly instead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For each Friday, the dates where that expiry sits about 30 days out are roughly 25 to 35 days before it:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">fridays = []\nday = START\nwhile day &lt;= END + timedelta(days=40):\n    if day.weekday() == 4:\n        fridays.append(day)\n    day += timedelta(days=1)\n\nrows_by_date = defaultdict(list)\n\nfor expiry in fridays:\n    spot = price_near(expiry - timedelta(days=30))\n    if spot is None:\n        continue\n    for row in fetch_expiry(expiry, round(spot * 0.94), round(spot * 1.06)):\n        a = row[\"attributes\"]\n        row_date = row[\"id\"][-10:]\n        if not a[\"volatility\"] or a[\"dte\"] is None:\n            continue\n        if 25 &lt;= a[\"dte\"] &lt;= 35 and START.isoformat() &lt;= row_date &lt;= END.isoformat():\n            rows_by_date[row_date].append(a)<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p class=\"wp-block-paragraph\">What we have now is a bucket of candidate contracts per trading date. Turning that into one number per day means picking the closest to the money \u2014 separately for the call and the put, then averaging the two.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Averaging the pair is a convention rather than a law, and it&#8217;s worth knowing what it papers over. At a single strike near the money one leg is slightly in the money and the other slightly out, and the in-the-money leg is the less trustworthy of the two: wider spread, and for American-style puts an early-exercise premium the model has to guess at. Averaging keeps skew from leaking into what&#8217;s meant to be a level, which is what we want here. If you need a cleaner number, work from the out-of-the-money side or from a forward-implied ATM strike.<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">series = []\n\nfor day in sorted(rows_by_date):\n    picks = {}\n    for kind in (\"call\", \"put\"):\n        same = [c for c in rows_by_date[day] if c[\"type\"] == kind]\n        if same:\n            # closest to the money first, then closest to 30 days\n            picks[kind] = min(same, key=lambda c: (abs(c[\"moneyness\"]), abs(c[\"dte\"] - 30)))\n\n    if not picks:\n        continue\n\n    ivs = [p[\"volatility\"] for p in picks.values()]\n    series.append({\n        \"date\": day,\n        \"iv\": round(sum(ivs) \/ len(ivs), 4),\n        \"iv_call\": picks.get(\"call\", {}).get(\"volatility\"),\n        \"iv_put\": picks.get(\"put\", {}).get(\"volatility\"),\n        \"strike\": picks[next(iter(picks))][\"strike\"],\n    })\n\nprint(f\"{len(series)} daily observations from {REQUESTS} requests\")<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p class=\"wp-block-paragraph\">For Apple over the twelve months to 1 September 2026 that prints:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">253 daily observations from 97 requests<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<p class=\"wp-block-paragraph\">Essentially every trading day in the window, for under a hundred requests. Your exact count will differ by a page or two and will creep up over time \u2014 each new trading day adds rows to every live expiry, so a band that fits in four pages today needs five next month. The observation count and the volatility figures are unaffected; only the request tally drifts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cheap enough to run across a watchlist without thinking about it \u2014 which is exactly what the naive version could not do at any price.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">What a year of Apple&#8217;s implied volatility shows<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the series, with Apple&#8217;s four earnings reports marked.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1500\" height=\"660\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png\" alt=\"AAPL at-the-money 30-day implied volatility, September 2025 to September 2026, with earnings dates marked\" class=\"wp-image-6861\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png 1500w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series-300x132.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series-1024x451.png 1024w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series-768x338.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series-60x26.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series-150x66.png 150w\" sizes=\"auto, (max-width: 1500px) 100vw, 1500px\" \/><figcaption class=\"wp-element-caption\">AAPL ATM 30-DTE implied volatility. Dashed lines are earnings reports.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Over the year, 30-day IV ranged from <strong>17.2% to 33.6%<\/strong>, with a median of 25.1%. That range is the context that was missing at the start: 25% isn&#8217;t &#8220;high&#8221; or &#8220;low&#8221; for Apple, it&#8217;s dead average. The last observation, 1 September 2026, came in at 24.7% \u2014 the <strong>47th percentile<\/strong> of the year. Neither cheap nor expensive. That&#8217;s a boring answer, and boring answers are worth a lot when the alternative is guessing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now look at what happens at those dashed lines. Every single one is followed by a cliff:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Earnings date<\/th><th>IV on the day<\/th><th>IV five sessions later<\/th><th>Change<\/th><th>Stock&#8217;s next-day move<\/th><\/tr><\/thead><tbody>\n<tr><td>30 Oct 2025<\/td><td>25.8%<\/td><td>23.6%<\/td><td>\u22122.1 pp<\/td><td>\u22120.38%<\/td><\/tr>\n<tr><td>29 Jan 2026<\/td><td>31.3%<\/td><td>27.0%<\/td><td>\u22124.3 pp<\/td><td>+0.46%<\/td><\/tr>\n<tr><td>30 Apr 2026<\/td><td>29.2%<\/td><td>22.8%<\/td><td>\u22126.4 pp<\/td><td>+3.24%<\/td><\/tr>\n<tr><td>30 Jul 2026<\/td><td>29.8%<\/td><td>24.8%<\/td><td>\u22125.0 pp<\/td><td>\u22127.35%<\/td><\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Four reports, four collapses, averaging <strong>4.5 percentage points in five sessions<\/strong>. This is the well-known &#8220;IV crush&#8221;, and it&#8217;s satisfying to watch it fall out of data you assembled yourself rather than read about it in a textbook.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Look at that last row, though. Apple dropped 7.35% the day after its July report \u2014 a big move, exactly the kind of thing you&#8217;d want to own options for. And implied volatility still fell 5 points. If you had bought a straddle into that print, you were right about the stock moving and could still have lost money, because you paid for volatility that evaporated the moment the uncertainty did. Direction was never the whole trade.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s something else the data says, and it contradicts what everybody repeats. The standard line is that IV ramps up into earnings. Across these four reports it didn&#8217;t: IV ten sessions before the announcement averaged <strong>0.4 points higher<\/strong> than on the day itself. Four reports is a small sample and I wouldn&#8217;t generalise from it \u2014 but in this window the run-up simply wasn&#8217;t there, while the collapse was, every time. That asymmetry is the kind of thing you can only check with history.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And the biggest volatility spike of the entire year had nothing to do with earnings at all. On 30 March 2026 Apple&#8217;s 30-day IV hit 33.6%, the high of the sample, with no report anywhere near. The stock sat at $246 and barely moved that week. Then it rallied to $272 by 20 April \u2014 up nearly 11%. The options market had priced a storm; what arrived was a rally. Implied volatility is the price of expected movement, not a prediction of which way, and it is frequently just wrong.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One more series worth plotting, almost free now that the pieces are in place. We kept the call and put IV separately, so subtracting one from the other gives a running measure of what downside protection costs relative to upside:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">gap = [\n    (s[\"date\"], (s[\"iv_put\"] - s[\"iv_call\"]) * 100)\n    for s in series\n    if s[\"iv_put\"] and s[\"iv_call\"]\n]<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1500\" height=\"600\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/3-aapl-atm-put-minus-call-iv.png\" alt=\"Difference between at-the-money put and call implied volatility for AAPL over one year\" class=\"wp-image-6863\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/3-aapl-atm-put-minus-call-iv.png 1500w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/3-aapl-atm-put-minus-call-iv-300x120.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/3-aapl-atm-put-minus-call-iv-1024x410.png 1024w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/3-aapl-atm-put-minus-call-iv-768x307.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/3-aapl-atm-put-minus-call-iv-60x24.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/3-aapl-atm-put-minus-call-iv-150x60.png 150w\" sizes=\"auto, (max-width: 1500px) 100vw, 1500px\" \/><figcaption class=\"wp-element-caption\">At-the-money put IV minus call IV. When it climbs, puts are getting relatively pricier.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And the practical version of the original question \u2014 a percentile, not a level:<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">def iv_percentile(series):\n    values = sorted(s[\"iv\"] for s in series)\n    latest = series[-1][\"iv\"]\n    rank = sum(1 for v in values if v &lt;= latest) \/ len(values)\n    return latest, rank\n\niv, rank = iv_percentile(series)\nprint(f\"latest IV {iv:.1%} \u2014 {rank:.0%} percentile of the window\")<\/code><\/pre>\n\n                <\/div>\n                <div class=\"code__btns\">\n                    <button class=\"code__copy\" class=\"copy\" title=\"Copy url\">\n                        <svg class=\"code__copy__icon\" width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                            <use xlink:href=\"\/img\/icons\/copy.svg#copy\"><\/use>\n                        <\/svg>\n                        <img decoding=\"async\" class=\"code__copy__approve\" alt=\"\" src=\"\/img\/approve_ico.svg\" loading=\"eager\">\n                    <\/button>\n                <\/div>\n            <\/div>\n        \n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1125\" height=\"600\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/4-aapl-30dte-iv-distribution.png\" alt=\"Distribution of AAPL 30-day implied volatility over one year with the latest reading marked\" class=\"wp-image-6864\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/4-aapl-30dte-iv-distribution.png 1125w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/4-aapl-30dte-iv-distribution-300x160.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/4-aapl-30dte-iv-distribution-1024x546.png 1024w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/4-aapl-30dte-iv-distribution-768x410.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/4-aapl-30dte-iv-distribution-60x32.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/4-aapl-30dte-iv-distribution-150x80.png 150w\" sizes=\"auto, (max-width: 1125px) 100vw, 1125px\" \/><figcaption class=\"wp-element-caption\">A year of readings in one picture: the latest value against the full distribution.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">&#8220;Apple&#8217;s 30-day IV is at the 47th percentile of the past year&#8221; is a sentence you can act on. &#8220;Apple&#8217;s IV is 24.7%&#8221; is not.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Change the symbol to AMZN and the same code gives you Amazon&#8217;s volatility index. Put the two side by side and you get the clearest possible argument for why levels are useless on their own.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1500\" height=\"660\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/2-aapl-vs-amzn-atm-30dte-iv.png\" alt=\"AAPL and AMZN at-the-money 30-day implied volatility compared over one year\" class=\"wp-image-6862\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/2-aapl-vs-amzn-atm-30dte-iv.png 1500w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/2-aapl-vs-amzn-atm-30dte-iv-300x132.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/2-aapl-vs-amzn-atm-30dte-iv-1024x451.png 1024w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/2-aapl-vs-amzn-atm-30dte-iv-768x338.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/2-aapl-vs-amzn-atm-30dte-iv-60x26.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/2-aapl-vs-amzn-atm-30dte-iv-150x66.png 150w\" sizes=\"auto, (max-width: 1500px) 100vw, 1500px\" \/><figcaption class=\"wp-element-caption\">Same code, two tickers. Amazon&#8217;s 30-day IV sat above Apple&#8217;s on every day of the sample.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Amazon&#8217;s 30-day IV sat above Apple&#8217;s on <strong>252 of the 253 days<\/strong> \u2014 every day but one \u2014 averaging 9.3 points higher, median 32.8% against 25.1%. Now look what that does to the original question. On 1 September 2026 Amazon printed 28.8% and Apple 24.7%. The bigger number is the <em>cheaper<\/em> one: 28.8% is Amazon&#8217;s 22nd percentile for the year, while Apple&#8217;s 24.7% is its 47th. Sort a watchlist by raw implied volatility and you will get Amazon above Apple every single day, which tells you nothing you didn&#8217;t know. Rank each name against its own history and you find out which options are actually on sale.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Where this breaks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Four things to keep in mind before you lean on this.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>It&#8217;s end-of-day, and that&#8217;s a feature here.<\/strong> One clean reading per session is exactly what you want for a historical series. But it means you cannot use this to react intraday, and you shouldn&#8217;t pretend otherwise \u2014 if your idea depends on where IV was at 11:30, this dataset can&#8217;t tell you.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The 25-to-35-day window is a compromise.<\/strong> Some days you&#8217;ll land on a 26-day contract and some on a 34-day one, and since the volatility term structure slopes, that introduces a little jitter that isn&#8217;t real vol movement. If it bothers you, interpolate between the two nearest expiries to hit exactly 30 days. You have everything you need to do that; I left it out to keep the code readable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Nearest-to-the-money is not the same as at-the-money.<\/strong> When strikes are spaced $5 apart and the stock sits between two of them, your &#8220;ATM&#8221; contract is up to $2.50 off. On a $250 stock that&#8217;s noise; on a $12 stock it isn&#8217;t. This is also why the band width mattered: get it wrong and you don&#8217;t get an error, you get a slightly worse contract and a slightly wrong number.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Liquidity decides whether any of this means anything.<\/strong> Apple and Amazon have deep, tightly quoted chains, so their implied volatility is a real market price. Go far enough down the market-cap ladder and you&#8217;ll find contracts whose IV is derived from a quote nobody would trade against. The data will happily hand you a number; the bid-ask spread, volume and open interest fields are how you decide whether to believe it. Filter on them before you trust a thin name.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Run it on your own ticker<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Everything above runs on the demo token, which covers AAPL and AMZN without registration \u2014 including the price history call. Change one variable and you have Amazon&#8217;s volatility index instead. Copy the code, run it, and you&#8217;ll have a year of ticker-level IV history in a couple of minutes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beyond those two tickers you&#8217;ll need a key. The <a href=\"https:\/\/eodhd.com\/marketplace\/unicornbay\/options\">US Stock Options Data API<\/a> covers roughly 6,900 US underlyings with history back to October 2023, and every contract row carries implied volatility, all five Greeks, open interest and bid\/ask alongside the days-to-expiry and moneyness fields we leaned on here. The <a href=\"https:\/\/eodhd.com\/marketplace\/unicornbay\/options\/docs\">API documentation<\/a> has the full parameter list.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two directions worth taking this next. Run the loop across a basket and you can rank names by how expensive their options are relative to their own history \u2014 a far more useful screen than sorting by raw IV, which just surfaces the most volatile stocks every time. Or hold the date fixed and vary strike and expiry instead of pinning them, and the same rows give you the volatility surface: skew across strikes, term structure across expiries.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next time I want to go after a noisier question \u2014 spotting unusual options activity from end-of-day data, and being honest about what &#8220;unusual&#8221; can and can&#8217;t mean when you only see the close.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>Here is a question that sounds simple and turns out to be surprisingly hard to answer: are options on Apple expensive right now? Your broker will happily show you today&#8217;s implied volatility. Fine \u2014 but 25% implied volatility means nothing on its own. Is that high for Apple? Low? Perfectly ordinary for a Tuesday in [&hellip;]<\/p>\n","protected":false},"author":25,"featured_media":6861,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[93],"tags":[],"coding-language":[],"ready-to-go-solution":[],"qualification":[],"financial-apis-category":[],"financial-apis-manuals":[],"class_list":["post-6860","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-stock-options","has_thumb"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v21.9 (Yoast SEO v26.7) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>How to Build a Historical Implied Volatility Series for Any US Stock (Python) | EODHD APIs Academy<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Build a Historical Implied Volatility Series for Any US Stock (Python)\" \/>\n<meta property=\"og:description\" content=\"Here is a question that sounds simple and turns out to be surprisingly hard to answer: are options on Apple expensive right now? Your broker will happily show you today&#8217;s implied volatility. Fine \u2014 but 25% implied volatility means nothing on its own. Is that high for Apple? Low? Perfectly ordinary for a Tuesday in [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python\" \/>\n<meta property=\"og:site_name\" content=\"Financial Academy\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/eodhistoricaldata\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-04T10:48:40+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-04T11:02:31+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1500\" \/>\n\t<meta property=\"og:image:height\" content=\"660\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Alex P\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@EOD_data\" \/>\n<meta name=\"twitter:site\" content=\"@EOD_data\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Alex P\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#article\",\"isPartOf\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python\"},\"author\":{\"name\":\"Alex P\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/972e0eccbe8bc4426d1ba924517fb3b9\"},\"headline\":\"How to Build a Historical Implied Volatility Series for Any US Stock (Python)\",\"datePublished\":\"2026-09-04T10:48:40+00:00\",\"dateModified\":\"2026-09-04T11:02:31+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python\"},\"wordCount\":2920,\"publisher\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#organization\"},\"image\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#primaryimage\"},\"thumbnailUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png\",\"articleSection\":[\"Stock Options\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python\",\"name\":\"How to Build a Historical Implied Volatility Series for Any US Stock (Python) | EODHD APIs Academy\",\"isPartOf\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#primaryimage\"},\"image\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#primaryimage\"},\"thumbnailUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png\",\"datePublished\":\"2026-09-04T10:48:40+00:00\",\"dateModified\":\"2026-09-04T11:02:31+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#primaryimage\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png\",\"contentUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png\",\"width\":1500,\"height\":660},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/eodhd.com\/financial-academy\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Build a Historical Implied Volatility Series for Any US Stock (Python)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#website\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/\",\"name\":\"Financial APIs Academy | EODHD\",\"description\":\"Financial Stock Market Academy\",\"publisher\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/eodhd.com\/financial-academy\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#organization\",\"name\":\"EODHD (EOD Historical Data)\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/12\/EODHD-Logo.png\",\"contentUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/12\/EODHD-Logo.png\",\"width\":159,\"height\":82,\"caption\":\"EODHD (EOD Historical Data)\"},\"image\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/eodhistoricaldata\",\"https:\/\/x.com\/EOD_data\",\"https:\/\/www.reddit.com\/r\/EODHistoricalData\/\",\"https:\/\/eod-historical-data.medium.com\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/972e0eccbe8bc4426d1ba924517fb3b9\",\"name\":\"Alex P\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/020e454749b61223b72b1bf96e7978ccdd1e39b04585b29698da3767a193a57d?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/020e454749b61223b72b1bf96e7978ccdd1e39b04585b29698da3767a193a57d?s=96&d=mm&r=g\",\"caption\":\"Alex P\"},\"description\":\"Product Manager at EODHD.\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/author\/a-pletnev\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How to Build a Historical Implied Volatility Series for Any US Stock (Python) | EODHD APIs Academy","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python","og_locale":"en_US","og_type":"article","og_title":"How to Build a Historical Implied Volatility Series for Any US Stock (Python)","og_description":"Here is a question that sounds simple and turns out to be surprisingly hard to answer: are options on Apple expensive right now? Your broker will happily show you today&#8217;s implied volatility. Fine \u2014 but 25% implied volatility means nothing on its own. Is that high for Apple? Low? Perfectly ordinary for a Tuesday in [&hellip;]","og_url":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python","og_site_name":"Financial Academy","article_publisher":"https:\/\/www.facebook.com\/eodhistoricaldata","article_published_time":"2026-09-04T10:48:40+00:00","article_modified_time":"2026-09-04T11:02:31+00:00","og_image":[{"width":1500,"height":660,"url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png","type":"image\/png"}],"author":"Alex P","twitter_card":"summary_large_image","twitter_creator":"@EOD_data","twitter_site":"@EOD_data","twitter_misc":{"Written by":"Alex P","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#article","isPartOf":{"@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python"},"author":{"name":"Alex P","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/972e0eccbe8bc4426d1ba924517fb3b9"},"headline":"How to Build a Historical Implied Volatility Series for Any US Stock (Python)","datePublished":"2026-09-04T10:48:40+00:00","dateModified":"2026-09-04T11:02:31+00:00","mainEntityOfPage":{"@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python"},"wordCount":2920,"publisher":{"@id":"https:\/\/eodhd.com\/financial-academy\/#organization"},"image":{"@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#primaryimage"},"thumbnailUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png","articleSection":["Stock Options"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python","url":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python","name":"How to Build a Historical Implied Volatility Series for Any US Stock (Python) | EODHD APIs Academy","isPartOf":{"@id":"https:\/\/eodhd.com\/financial-academy\/#website"},"primaryImageOfPage":{"@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#primaryimage"},"image":{"@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#primaryimage"},"thumbnailUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png","datePublished":"2026-09-04T10:48:40+00:00","dateModified":"2026-09-04T11:02:31+00:00","breadcrumb":{"@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#primaryimage","url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png","contentUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png","width":1500,"height":660},{"@type":"BreadcrumbList","@id":"https:\/\/eodhd.com\/financial-academy\/stock-options\/build-your-own-volatility-index-python#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/eodhd.com\/financial-academy\/"},{"@type":"ListItem","position":2,"name":"How to Build a Historical Implied Volatility Series for Any US Stock (Python)"}]},{"@type":"WebSite","@id":"https:\/\/eodhd.com\/financial-academy\/#website","url":"https:\/\/eodhd.com\/financial-academy\/","name":"Financial APIs Academy | EODHD","description":"Financial Stock Market Academy","publisher":{"@id":"https:\/\/eodhd.com\/financial-academy\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/eodhd.com\/financial-academy\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/eodhd.com\/financial-academy\/#organization","name":"EODHD (EOD Historical Data)","url":"https:\/\/eodhd.com\/financial-academy\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/logo\/image\/","url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/12\/EODHD-Logo.png","contentUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2023\/12\/EODHD-Logo.png","width":159,"height":82,"caption":"EODHD (EOD Historical Data)"},"image":{"@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/eodhistoricaldata","https:\/\/x.com\/EOD_data","https:\/\/www.reddit.com\/r\/EODHistoricalData\/","https:\/\/eod-historical-data.medium.com\/"]},{"@type":"Person","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/972e0eccbe8bc4426d1ba924517fb3b9","name":"Alex P","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/020e454749b61223b72b1bf96e7978ccdd1e39b04585b29698da3767a193a57d?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/020e454749b61223b72b1bf96e7978ccdd1e39b04585b29698da3767a193a57d?s=96&d=mm&r=g","caption":"Alex P"},"description":"Product Manager at EODHD.","url":"https:\/\/eodhd.com\/financial-academy\/author\/a-pletnev"}]}},"jetpack_featured_media_url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2026\/09\/1-aapl-atm-30dte-iv-series.png","jetpack_shortlink":"https:\/\/wp.me\/pdOdVT-1ME","jetpack_sharing_enabled":true,"acf":[],"_links":{"self":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts\/6860","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/comments?post=6860"}],"version-history":[{"count":6,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts\/6860\/revisions"}],"predecessor-version":[{"id":6871,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts\/6860\/revisions\/6871"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/media\/6861"}],"wp:attachment":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/media?parent=6860"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/categories?post=6860"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/tags?post=6860"},{"taxonomy":"coding-language","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/coding-language?post=6860"},{"taxonomy":"ready-to-go-solution","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/ready-to-go-solution?post=6860"},{"taxonomy":"qualification","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/qualification?post=6860"},{"taxonomy":"financial-apis-category","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/financial-apis-category?post=6860"},{"taxonomy":"financial-apis-manuals","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/financial-apis-manuals?post=6860"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}