{"id":6538,"date":"2025-09-24T06:59:16","date_gmt":"2025-09-24T06:59:16","guid":{"rendered":"https:\/\/eodhd.com\/financial-academy\/?p=6538"},"modified":"2025-09-24T06:59:18","modified_gmt":"2025-09-24T06:59:18","slug":"backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python","status":"publish","type":"post","link":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python","title":{"rendered":"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with Python"},"content":{"rendered":"\n<p id=\"5f36\">We all learn early on that taking on more risk can lead to bigger rewards. Basically, this means that the more a stock price swings, the greater its potential gain. Does that sound familiar? But here&#8217;s an interesting twist: many traders talk about something called the low-volatility anomaly. This idea suggests that stocks with less price fluctuation can sometimes give you even better returns when you consider the risk involved, compared to their more volatile peers.<\/p>\n\n\n\n<p id=\"e8b6\">Here, I&#8217;ll put this theory to the test by attempting to identify the anomaly. The approach will involve calculating rolling volatility from daily stock prices, ranking stocks monthly, and building an equal-weighted portfolio of the least and most volatile stocks, using data from the EODHD&nbsp;suite of APIs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"a1a7\">Let\u2019s write some Python<\/h3>\n\n\n\n<p id=\"1be6\">Let&#8217;s set up our imports and parameters<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">import os\nfrom tqdm import tqdm\nimport pandas as pd\nimport numpy as np\nimport requests\nimport requests_cache\nimport matplotlib.pyplot as plt\n\napi_token = os.environ.get('EODHD_API_TOKEN')\n\nSTART_DATE = '2015-01-01'\nUNIVERSE_INDEX = 'GSPC.INDX'  # EODHD index code for S&amp;P 500\nN_VOL = 10\nROLLING_WINDOW = 22  # ~1 month trading days<\/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>As you can see, we&#8217;ll use the&nbsp;<a href=\"https:\/\/eodhd.com\/lp\/historical-eod-api?via=phitzi\" target=\"_blank\" rel=\"noreferrer noopener\">EODHD API<\/a>&nbsp;suite to retrieve the last 10 years of prices to date. For our backtesting universe, we&#8217;ll obtain the SP500 stocks. EODHD provides an endpoint that enumerates the current constituents of the S&amp;P 500 as well as their historical memberships, including the respective start and end dates during which each stock was part of this prestigious index.<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">url = 'https:\/\/eodhd.com\/api\/mp\/unicornbay\/spglobal\/comp\/{}'.format(UNIVERSE_INDEX)\nr = requests.get(url, params={'api_token': api_token, 'fmt': 'json'})\ndata = r.json()\ndf_symbols = pd.DataFrame(data['HistoricalTickerComponents']).T.reset_index()\ndf_symbols<\/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-full\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"224\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png\" alt=\"\" class=\"wp-image-6539\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png 786w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-300x85.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-768x219.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-60x17.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-150x43.png 150w\" sizes=\"auto, (max-width: 786px) 100vw, 786px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>That will give us a total of 794 stocks that were part of the SP500 over the years. We&#8217;ll also add the sector using the relevant\u00a0<a href=\"https:\/\/eodhd.com\/lp\/historical-eod-api?via=phitzi\" target=\"_blank\" rel=\"noreferrer noopener\">EODHD<\/a>\u00a0fundamentals endpoint, so we can get the sector in our analysis later.<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">def fetch_fundamentals(code, exchange='US'):\n    ticker = f\"{code}.{exchange}\"\n    url = f'https:\/\/eodhd.com\/api\/fundamentals\/{ticker}'\n    try:\n        resp = requests.get(url, params={'api_token': api_token, 'fmt': 'json'}, timeout=30)\n        if resp.status_code != 200:\n            return None, None\n        js = resp.json() if resp.content else {}\n        general = js.get('General', {}) if isinstance(js, dict) else {}\n        highlights = js.get('Highlights', {}) if isinstance(js, dict) else {}\n        sector = general.get('Sector')\n        return sector\n    except Exception:\n        return None\n\n\nrecords = []\nfor code in tqdm(df_symbols['Code'].astype(str).unique(), desc='Fetching fundamentals'):\n    sector = fetch_fundamentals(code, 'US')\n    records.append({'Code': code, 'Sector': sector})\n\nfund_df = pd.DataFrame(records)\ndf_symbols = df_symbols.drop(columns=[c for c in ['Sector'] if c in df_symbols.columns]) \\\n    .merge(fund_df, on='Code', how='left')\n\nsymbols = (df_symbols['Code'].astype(str) + '.US').tolist()<\/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>Now, let&#8217;s dive into the main analysis. First, I&#8217;ll create a dataframe called all_adj_close&nbsp;to work with the adjusted closing prices from the last 10 years, giving us a solid foundation for our insights.<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\"># Function to fetch data for a single ticker\ndef fetch_stock_data(ticker, start_date):\n    url = f'https:\/\/eodhd.com\/api\/eod\/{ticker}'\n    query = {\n        'api_token': api_token,\n        'fmt': 'json',\n        'from': start_date,\n    }\n    url = f'https:\/\/eodhd.com\/api\/eod\/{ticker}'\n    query = {\n        'api_token': api_token,\n        'fmt': 'json',\n        'from': start_date,\n    }\n\n    response = requests.get(url, params=query)\n    if response.status_code != 200:\n        print(f\"Error fetching data for {ticker}: {response.status_code}\")\n        print(response.text)\n        return None\n\n    try:\n        data = response.json()\n        df = pd.DataFrame(data)\n        df['date'] = pd.to_datetime(df['date'])\n        df.set_index('date', inplace=True)\n        df.sort_index(inplace=True)  # Ensure data is sorted by date\n        return df\n    except Exception as e:\n        return None\n\n\n# Create an empty DataFrame to store adjusted close prices\nall_adj_close = pd.DataFrame()\n\n# Prepare SP500 membership intervals per ticker (from df_symbols)\ntmp_members = df_symbols[['Code', 'StartDate', 'EndDate']].copy()\ntmp_members['StartDate'] = pd.to_datetime(tmp_members['StartDate'], errors='coerce')\ntmp_members['EndDate'] = pd.to_datetime(tmp_members['EndDate'], errors='coerce')  # NaT means still active\nmembership_ranges = {}\nfor _, row in tmp_members.iterrows():\n    code = str(row['Code'])\n    start = row['StartDate']\n    end = row['EndDate']  # can be NaT\n    if pd.isna(start):\n        continue\n    membership_ranges.setdefault(code, []).append((start, end))\n\n# Fetch data for each ticker and add to the DataFrame\nfor ticker in tqdm(symbols):\n    stock_data = fetch_stock_data(ticker, START_DATE)\n    if stock_data is not None:\n        # Extract ticker symbol without exchange suffix for column name\n        symbol = ticker.split('.')[0]\n        all_adj_close[symbol] = stock_data['adjusted_close']\n\n\n        # Create SP500 membership indicator per date for this ticker\n        dates = stock_data.index\n        in_sp500 = pd.Series(False, index=dates)\n        for (start, end) in membership_ranges.get(symbol, []):\n            if pd.isna(end):\n                mask = dates &gt;= start\n            else:\n                mask = (dates &gt;= start) &amp; (dates &lt;= end)\n            if mask.any():\n                in_sp500.loc[mask] = True\n        all_adj_close[symbol + \"_IN_SP500\"] = in_sp500.astype(int)  # 1 if in SP500, 0 otherwise\n\n        rets = stock_data['adjusted_close'].pct_change()\n        all_adj_close[symbol + \"_PCT_CHANGE\"] = rets\n        vol = rets.rolling(ROLLING_WINDOW).std()\n        all_adj_close[symbol + \"_VOL\"] = vol\n    else:\n        pass<\/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 id=\"050c\">Let me explain what we are doing here:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>For each symbol, we&#8217;ll gather the daily closing prices at the end of the day. Our dataframe will have one column for each symbol, named after it. For example, AAPL.<\/li>\n\n\n\n<li>Additionally, since we&#8217;re there, we&#8217;ll also calculate the daily percentage change (e.g. AAPL_PCT_CHANGE), which helps us determine the rolling volatility over 22 days (roughly a month), referred to as AAPL_VOL in our example.<\/li>\n\n\n\n<li>Additionally, we&#8217;ll have a field for each ticker ending in _IN_SP500, which shows whether the specific stock was included in the S&amp;P 500 on that day.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"665\" height=\"303\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-1.png\" alt=\"\" class=\"wp-image-6540\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-1.png 665w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-1-300x137.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-1-60x27.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-1-150x68.png 150w\" sizes=\"auto, (max-width: 665px) 100vw, 665px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p id=\"1b02\">Next, we need to create two columns for the highest and lowest volatility tickers, as comma-separated strings, at the start of each month. These will only include stocks that were part of the S&amp;P 500 on the first day.<\/p>\n\n\n\n<p id=\"b752\">Since our investment portfolio doesn&#8217;t need changes daily, we&#8217;ll rebalance it once a month. This way, we can stay focused and ensure everything stays on track.<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">dates_first = all_adj_close.index[all_adj_close.index.day == 1].unique()\n\nrecords = []\nfor d in dates_first:\n    # Identify volatility columns where the equivalent _IN_SP500 is 1 on date d\n    vol_cols = [\n        c for c in all_adj_close.columns\n        if c.endswith('_VOL') and all_adj_close.at[d, c[:-4] + '_IN_SP500'] == 1\n    ]\n\n    s = all_adj_close.loc[d, vol_cols].dropna()\n    if s.empty:\n        continue\n\n    # Highest and lowest volatility tickers for the date\n    highest = s.sort_values(ascending=False).head(N_VOL).index\n    lowest = s.sort_values(ascending=True).head(N_VOL).index\n\n    # Strip the _VOL suffix and join with commas\n    highest_str = \",\".join([col[:-4] for col in highest])  # remove '_VOL'\n    lowest_str = \",\".join([col[:-4] for col in lowest])\n\n    records.append({\n        'DATE': d,\n        'HIGHEST_VOL_TICKERS': highest_str,\n        'LOWEST_VOL_TICKERS': lowest_str\n    })\n\nmonthly_vol_tickers = pd.DataFrame.from_records(records).set_index('DATE').sort_index()\nmonthly_vol_tickers<\/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-full\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"331\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-2.png\" alt=\"\" class=\"wp-image-6541\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-2.png 786w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-2-300x126.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-2-768x323.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-2-60x25.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-2-150x63.png 150w\" sizes=\"auto, (max-width: 786px) 100vw, 786px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Here&#8217;s a dataframe with one row for each month. I&#8217;ll add this information to the original dataframe that includes all the prices by month.<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">mt = monthly_vol_tickers.copy()\nmt['MONTH'] = mt.index.to_period('M')\n\ntop_map = mt.groupby('MONTH')['HIGHEST_VOL_TICKERS'].last().to_dict()\nlow_map = mt.groupby('MONTH')['LOWEST_VOL_TICKERS'].last().to_dict()\n\n# Map each date in all_adj_close to its month and fill columns for the entire month\nperiods = all_adj_close.index.to_period('M')\nall_adj_close['HIGHEST_VOL_TICKERS'] = periods.map(top_map)\nall_adj_close['LOWEST_VOL_TICKERS'] = periods.map(low_map)<\/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 id=\"c193\">The next step is to calculate the daily percentage change of the stocks we are invested in, as well as the equity curve. Note that:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>For the highest and lowest stocks, I&#8217;m using the average percentage change to calculate returns and the equity curve, as we&#8217;re assuming equal investment in each stock.<\/li>\n\n\n\n<li>I&#8217;ll also use the S&amp;P 500 index as a reference, but with a small tweak. Instead of relying on the index itself, which is weighted by each stock&#8217;s market capitalisation, I&#8217;ll calculate the average of the percentage change and equity values for all the symbols in the S&amp;P 500 on that day. This way, we can make a fairer comparison, like comparing apples with apples.<\/li>\n<\/ul>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\"># Prepare symbol to column mappings\n_PCT_SUFFIX = '_PCT_CHANGE'\n_IN_SUFFIX = '_IN_SP500'\n\npct_cols = [c for c in all_adj_close.columns if c.endswith(_PCT_SUFFIX)]\nsymbols_from_pct = [c[:-len(_PCT_SUFFIX)] for c in pct_cols]\n\npct_col_by_symbol = {sym: f'{sym}{_PCT_SUFFIX}' for sym in symbols_from_pct}\nin_col_by_symbol = {sym: f'{sym}{_IN_SUFFIX}' for sym in symbols_from_pct}\n\n\ndef avg_for_list(row, list_col_name):\n    syms_str = row.get(list_col_name, np.nan)\n    if not isinstance(syms_str, str) or not syms_str:\n        return np.nan\n    syms = [s.strip() for s in syms_str.split(',') if s.strip()]\n    cols = [pct_col_by_symbol[s] for s in syms if s in pct_col_by_symbol]\n    if not cols:\n        return np.nan\n    return row[cols].mean(skipna=True)\n\n\ndef avg_for_in_sp500(row):\n    syms_in = [s for s in symbols_from_pct if in_col_by_symbol[s] in row.index and row[in_col_by_symbol[s]] == 1]\n    cols = [pct_col_by_symbol[s] for s in syms_in if pct_col_by_symbol[s] in row.index]\n    if not cols:\n        return np.nan\n    return row[cols].mean(skipna=True)\n\n\n# Compute new columns\nall_adj_close['AVG_PCT_CHANGE_HIGHEST_VOL'] = all_adj_close.apply(lambda r: avg_for_list(r, 'HIGHEST_VOL_TICKERS'),\n                                                                  axis=1)\nall_adj_close['AVG_PCT_CHANGE_LOWEST_VOL'] = all_adj_close.apply(lambda r: avg_for_list(r, 'LOWEST_VOL_TICKERS'),\n                                                                 axis=1)\nall_adj_close['AVG_PCT_CHANGE_IN_SP500'] = all_adj_close.apply(avg_for_in_sp500, axis=1)\n\nall_adj_close[['AVG_PCT_CHANGE_HIGHEST_VOL', 'AVG_PCT_CHANGE_LOWEST_VOL', 'AVG_PCT_CHANGE_IN_SP500']]\n\ncols = ['AVG_PCT_CHANGE_HIGHEST_VOL', 'AVG_PCT_CHANGE_LOWEST_VOL', 'AVG_PCT_CHANGE_IN_SP500']\nrets = all_adj_close[cols].fillna(0.0)\nequity_curves = (1.0 + rets).cumprod()<\/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 id=\"968f\">Now let\u2019s plot\u2026<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">plt.figure(figsize=(12, 6))\nfor c in equity_curves.columns:\n    plt.plot(equity_curves.index, equity_curves[c], label=c)\nplt.title('Equity Curves')\nplt.xlabel('Date')\nplt.ylabel('Equity (Start=1.0)')\nplt.legend()\nplt.grid(True)\nplt.tight_layout()\nplt.show()<\/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 is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"476\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-3-1024x476.png\" alt=\"\" class=\"wp-image-6542\" style=\"width:840px;height:auto\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-3-1024x476.png 1024w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-3-300x139.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-3-768x357.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-3-60x28.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-3-150x70.png 150w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-3.png 1192w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p id=\"0296\">As you can see, the anomaly we&#8217;re discussing can&#8217;t be identified and doesn&#8217;t appear in the long run. What the theory states is also shown in the graph:<\/p>\n\n\n\n<p id=\"9dee\">Higher returns are linked to higher volatility, while lower returns are associated with lower volatility. The S&amp;P 500, with equally weighted returns, falls somewhere in between.<\/p>\n\n\n\n<p id=\"4c78\">So, let\u2019s see what happens if we apply the same concept to specific sectors. The only thing you need to do is add the following code right after you finish and run the code again.<\/p>\n\n\n\n            <div class=\"code__wrapper\">\n                <div class=\"code__content\">\n                    \n<pre class=\"wp-block-code\"><code class=\"\">df_symbols = df_symbols[df_symbols['Sector'].isin(['Technology'])].copy()<\/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-full\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"383\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-4.png\" alt=\"\" class=\"wp-image-6543\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-4.png 786w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-4-300x146.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-4-768x374.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-4-60x29.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-4-150x73.png 150w\" sizes=\"auto, (max-width: 786px) 100vw, 786px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p id=\"b3b5\">That&#8217;s really interesting! In the tech world, while SP_500 stocks usually do quite well, it&#8217;s so fascinating to see that the stocks with the lowest volatility often perform better than those with higher volatility. I believe this might be because technology stock fundamentals change so rapidly, making a stock swing from high to low volatility in just a few months. It seems like sticking to the middle range might be the safest and smartest bet here ;).<\/p>\n\n\n\n<p id=\"571b\">Let\u2019s see what happens with Financial Services.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"786\" height=\"382\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-5.png\" alt=\"\" class=\"wp-image-6544\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-5.png 786w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-5-300x146.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-5-768x373.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-5-60x29.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-5-150x73.png 150w\" sizes=\"auto, (max-width: 786px) 100vw, 786px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p id=\"3677\">Nevertheless, the SP_500 outperforms all of them in this case. The interesting part is that, with Financial Services, it is very clear that there are huge returns and drawdowns for the high-volatility stocks, while the low-volatility ones tend to move more slowly at the same time. It appears that Financial Services stocks tend to move more smoothly, making the low-volatility stocks more attractive.<\/p>\n\n\n\n<p id=\"643e\">What about Healthcare?<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"498\" src=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-6-1024x498.png\" alt=\"\" class=\"wp-image-6545\" srcset=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-6-1024x498.png 1024w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-6-300x146.png 300w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-6-768x373.png 768w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-6-60x29.png 60w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-6-150x73.png 150w, https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image-6.png 1185w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p id=\"8a0d\">That&#8217;s also interesting. High-volatility healthcare stocks don&#8217;t seem to follow the typical higher-risk, higher-reward rule. Since healthcare is a defensive sector with steady demand, the stocks still experience significant peaks and troughs due to regulatory decisions, drug trial results, and similar factors. Personally, I avoid volatile healthcare stocks.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"08cb\">Conclusions<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Over 10 years, S&amp;P 500 stocks showed higher returns for high-volatility portfolios and low-volatility anomaly was not detected.<\/li>\n\n\n\n<li>Within Technology, low-volatility stocks performed better than high-volatility stocks, suggesting sector-specific effects on the anomaly.<\/li>\n\n\n\n<li>Financial Services: high-volatility stocks had significant swings but fell behind the equally-weighted S&amp;P 500; low-volatility stocks maintained steady movement.<\/li>\n\n\n\n<li>Healthcare stocks with high volatility failed to outperform, probably due to regulatory and drug trial factors that led to unpredictable returns.<\/li>\n<\/ul>\n\n\n\n<p id=\"ea06\">What I took away from this analysis for myself is that the low-volatility anomaly isn&#8217;t a universal phenomenon. Sector dynamics and fundamentals have a significant impact on risk-return relationships. <\/p>\n\n\n\n<p id=\"ea06\">Thank you for reading. I hope you enjoyed it!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We all learn early on that taking on more risk can lead to bigger rewards. Basically, this means that the more a stock price swings, the greater its potential gain. Does that sound familiar? But here&#8217;s an interesting twist: many traders talk about something called the low-volatility anomaly. This idea suggests that stocks with less [&hellip;]<\/p>\n","protected":false},"author":30,"featured_media":0,"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":[59],"tags":[],"coding-language":[],"ready-to-go-solution":[],"qualification":[],"financial-apis-category":[],"financial-apis-manuals":[],"class_list":["post-6538","post","type-post","status-publish","format-standard","hentry","category-technical-analysis-examples"],"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>Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with 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\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with Python\" \/>\n<meta property=\"og:description\" content=\"We all learn early on that taking on more risk can lead to bigger rewards. Basically, this means that the more a stock price swings, the greater its potential gain. Does that sound familiar? But here&#8217;s an interesting twist: many traders talk about something called the low-volatility anomaly. This idea suggests that stocks with less [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-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=\"2025-09-24T06:59:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-09-24T06:59:18+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png\" \/>\n<meta name=\"author\" content=\"Filippos Tzimopoulos\" \/>\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=\"Filippos Tzimopoulos\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#article\",\"isPartOf\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python\"},\"author\":{\"name\":\"Filippos Tzimopoulos\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/62bea60a15bb6cf5f09511bbcad2c448\"},\"headline\":\"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with Python\",\"datePublished\":\"2025-09-24T06:59:16+00:00\",\"dateModified\":\"2025-09-24T06:59:18+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python\"},\"wordCount\":1035,\"publisher\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#organization\"},\"image\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#primaryimage\"},\"thumbnailUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png\",\"articleSection\":[\"Technical Analysis Examples\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python\",\"name\":\"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with Python | EODHD APIs Academy\",\"isPartOf\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#primaryimage\"},\"image\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#primaryimage\"},\"thumbnailUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png\",\"datePublished\":\"2025-09-24T06:59:16+00:00\",\"dateModified\":\"2025-09-24T06:59:18+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#primaryimage\",\"url\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png\",\"contentUrl\":\"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png\",\"width\":786,\"height\":224},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/eodhd.com\/financial-academy\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with 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\/62bea60a15bb6cf5f09511bbcad2c448\",\"name\":\"Filippos Tzimopoulos\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/4c3f487ab4ce0733b492937a98b5f56bf39f6c2c5ff51e85f849f7d09d5405f2?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/4c3f487ab4ce0733b492937a98b5f56bf39f6c2c5ff51e85f849f7d09d5405f2?s=96&d=mm&r=g\",\"caption\":\"Filippos Tzimopoulos\"},\"description\":\"Seasoned Business and Data Analyst with over 30+ years of experience | Python advocate | Finance and Quantitative enthusiast\",\"sameAs\":[\"https:\/\/medium.com\/@phitzi\"],\"url\":\"https:\/\/eodhd.com\/financial-academy\/author\/filippos\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with 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\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python","og_locale":"en_US","og_type":"article","og_title":"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with Python","og_description":"We all learn early on that taking on more risk can lead to bigger rewards. Basically, this means that the more a stock price swings, the greater its potential gain. Does that sound familiar? But here&#8217;s an interesting twist: many traders talk about something called the low-volatility anomaly. This idea suggests that stocks with less [&hellip;]","og_url":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python","og_site_name":"Financial Academy","article_publisher":"https:\/\/www.facebook.com\/eodhistoricaldata","article_published_time":"2025-09-24T06:59:16+00:00","article_modified_time":"2025-09-24T06:59:18+00:00","og_image":[{"url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png","type":"","width":"","height":""}],"author":"Filippos Tzimopoulos","twitter_card":"summary_large_image","twitter_creator":"@EOD_data","twitter_site":"@EOD_data","twitter_misc":{"Written by":"Filippos Tzimopoulos","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#article","isPartOf":{"@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python"},"author":{"name":"Filippos Tzimopoulos","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/62bea60a15bb6cf5f09511bbcad2c448"},"headline":"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with Python","datePublished":"2025-09-24T06:59:16+00:00","dateModified":"2025-09-24T06:59:18+00:00","mainEntityOfPage":{"@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python"},"wordCount":1035,"publisher":{"@id":"https:\/\/eodhd.com\/financial-academy\/#organization"},"image":{"@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#primaryimage"},"thumbnailUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png","articleSection":["Technical Analysis Examples"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python","url":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python","name":"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with Python | EODHD APIs Academy","isPartOf":{"@id":"https:\/\/eodhd.com\/financial-academy\/#website"},"primaryImageOfPage":{"@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#primaryimage"},"image":{"@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#primaryimage"},"thumbnailUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png","datePublished":"2025-09-24T06:59:16+00:00","dateModified":"2025-09-24T06:59:18+00:00","breadcrumb":{"@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#primaryimage","url":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png","contentUrl":"https:\/\/eodhd.com\/financial-academy\/wp-content\/uploads\/2025\/09\/image.png","width":786,"height":224},{"@type":"BreadcrumbList","@id":"https:\/\/eodhd.com\/financial-academy\/technical-analysis-examples\/backtesting-the-low-volatility-anomaly-on-the-sp-500-with-python#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/eodhd.com\/financial-academy\/"},{"@type":"ListItem","position":2,"name":"Backtesting the Low-Volatility Anomaly on the S&amp;P 500 with 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\/62bea60a15bb6cf5f09511bbcad2c448","name":"Filippos Tzimopoulos","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/eodhd.com\/financial-academy\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/4c3f487ab4ce0733b492937a98b5f56bf39f6c2c5ff51e85f849f7d09d5405f2?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/4c3f487ab4ce0733b492937a98b5f56bf39f6c2c5ff51e85f849f7d09d5405f2?s=96&d=mm&r=g","caption":"Filippos Tzimopoulos"},"description":"Seasoned Business and Data Analyst with over 30+ years of experience | Python advocate | Finance and Quantitative enthusiast","sameAs":["https:\/\/medium.com\/@phitzi"],"url":"https:\/\/eodhd.com\/financial-academy\/author\/filippos"}]}},"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pdOdVT-1Hs","jetpack_sharing_enabled":true,"acf":[],"_links":{"self":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts\/6538","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\/30"}],"replies":[{"embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/comments?post=6538"}],"version-history":[{"count":4,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts\/6538\/revisions"}],"predecessor-version":[{"id":6549,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/posts\/6538\/revisions\/6549"}],"wp:attachment":[{"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/media?parent=6538"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/categories?post=6538"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/tags?post=6538"},{"taxonomy":"coding-language","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/coding-language?post=6538"},{"taxonomy":"ready-to-go-solution","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/ready-to-go-solution?post=6538"},{"taxonomy":"qualification","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/qualification?post=6538"},{"taxonomy":"financial-apis-category","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/financial-apis-category?post=6538"},{"taxonomy":"financial-apis-manuals","embeddable":true,"href":"https:\/\/eodhd.com\/financial-academy\/wp-json\/wp\/v2\/financial-apis-manuals?post=6538"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}