Investigating A VIX Trading Signal¶
Python Imports¶
In [1]:
# Standard Library
import datetime
import io
import os
import random
import sys
import warnings
from datetime import datetime, timedelta
from pathlib import Path
# Data Handling
import numpy as np
import pandas as pd
# Data Visualization
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import seaborn as sns
from matplotlib.ticker import FormatStrFormatter, FuncFormatter, MultipleLocator
# Data Sources
import yfinance as yf
# Statistical Analysis
import statsmodels.api as sm
# Machine Learning
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# Suppress warnings
warnings.filterwarnings("ignore")
Add Directories To Path¶
In [2]:
# Add the source subdirectory to the system path to allow import config from settings.py
current_directory = Path(os.getcwd())
website_base_directory = current_directory.parent.parent.parent
src_directory = website_base_directory / "src"
sys.path.append(str(src_directory)) if str(src_directory) not in sys.path else None
# Import settings.py
from settings import config
# Add configured directories from config to path
SOURCE_DIR = config("SOURCE_DIR")
sys.path.append(str(Path(SOURCE_DIR))) if str(Path(SOURCE_DIR)) not in sys.path else None
QUANT_FINANCE_RESEARCH_BASE_DIR = config("QUANT_FINANCE_RESEARCH_BASE_DIR")
sys.path.append(str(Path(QUANT_FINANCE_RESEARCH_BASE_DIR))) if str(Path(QUANT_FINANCE_RESEARCH_BASE_DIR)) not in sys.path else None
QUANT_FINANCE_RESEARCH_SOURCE_DIR = config("QUANT_FINANCE_RESEARCH_SOURCE_DIR")
sys.path.append(str(Path(QUANT_FINANCE_RESEARCH_SOURCE_DIR))) if str(Path(QUANT_FINANCE_RESEARCH_SOURCE_DIR)) not in sys.path else None
# Add other configured directories
BASE_DIR = config("BASE_DIR")
CONTENT_DIR = config("CONTENT_DIR")
POSTS_DIR = config("POSTS_DIR")
PAGES_DIR = config("PAGES_DIR")
PUBLIC_DIR = config("PUBLIC_DIR")
SOURCE_DIR = config("SOURCE_DIR")
DATA_DIR = config("DATA_DIR")
DATA_MANUAL_DIR = config("DATA_MANUAL_DIR")
# Print system path
for i, path in enumerate(sys.path):
print(f"{i}: {path}")
0: /usr/lib/python313.zip 1: /usr/lib/python3.13 2: /usr/lib/python3.13/lib-dynload 3: 4: /home/jared/python-virtual-envs/general_313/lib/python3.13/site-packages 5: /home/jared/Cloud_Storage/Dropbox/Websites/jaredszajkowski.github.io/src 6: /home/jared/Cloud_Storage/Dropbox/Quant_Finance_Research 7: /home/jared/Cloud_Storage/Dropbox/Quant_Finance_Research/src
Track Index Dependencies¶
In [3]:
# Create file to track markdown dependencies
dep_file = Path("index_dep.txt")
dep_file.write_text("")
Out[3]:
0
Python Functions¶
In [4]:
from calc_vix_trade_pnl import calc_vix_trade_pnl
from df_info import df_info
from df_info_markdown import df_info_markdown
from export_track_md_deps import export_track_md_deps
from load_data import load_data
from pandas_set_decimal_places import pandas_set_decimal_places
from plot_price import plot_price
from plot_stats import plot_stats
from plot_vix_with_trades import plot_vix_with_trades
from yf_pull_data import yf_pull_data
Data Overview (VIX)¶
Acquire CBOE Volatility Index (VIX) Data¶
In [5]:
yf_pull_data(
base_directory=DATA_DIR,
ticker="^VIX",
source="Yahoo_Finance",
asset_class="Indices",
excel_export=True,
pickle_export=True,
output_confirmation=True,
)
YF.download() has changed argument auto_adjust default to True
[*********************100%***********************] 1 of 1 completed
The first and last date of data for ^VIX is:
Close | High | Low | Open | Volume | |
---|---|---|---|---|---|
Date | |||||
1990-01-02 | 17.24 | 17.24 | 17.24 | 17.24 | 0 |
Close | High | Low | Open | Volume | |
---|---|---|---|---|---|
Date | |||||
2025-06-09 | 17.16 | 17.719999 | 16.82 | 17.690001 | 0 |
Yahoo Finance data complete for ^VIX --------------------
Out[5]:
Close | High | Low | Open | Volume | |
---|---|---|---|---|---|
Date | |||||
1990-01-02 | 17.240000 | 17.240000 | 17.240000 | 17.240000 | 0 |
1990-01-03 | 18.190001 | 18.190001 | 18.190001 | 18.190001 | 0 |
1990-01-04 | 19.219999 | 19.219999 | 19.219999 | 19.219999 | 0 |
1990-01-05 | 20.110001 | 20.110001 | 20.110001 | 20.110001 | 0 |
1990-01-08 | 20.260000 | 20.260000 | 20.260000 | 20.260000 | 0 |
... | ... | ... | ... | ... | ... |
2025-06-03 | 17.690001 | 19.209999 | 17.639999 | 18.830000 | 0 |
2025-06-04 | 17.610001 | 18.070000 | 17.410000 | 17.680000 | 0 |
2025-06-05 | 18.480000 | 18.799999 | 17.080000 | 17.680000 | 0 |
2025-06-06 | 16.770000 | 18.350000 | 16.650000 | 18.160000 | 0 |
2025-06-09 | 17.160000 | 17.719999 | 16.820000 | 17.690001 | 0 |
8925 rows × 5 columns
Load Data - VIX¶
In [6]:
# Set decimal places
pandas_set_decimal_places(2)
# VIX
vix = load_data(
base_directory=DATA_DIR,
ticker="^VIX",
source="Yahoo_Finance",
asset_class="Indices",
timeframe="Daily",
)
# Set 'Date' column as datetime
vix['Date'] = pd.to_datetime(vix['Date'])
# Drop 'Volume'
vix.drop(columns = {'Volume'}, inplace = True)
# Set Date as index
vix.set_index('Date', inplace = True)
# Check to see if there are any NaN values
vix[vix['High'].isna()]
# Forward fill to clean up missing data
vix['High'] = vix['High'].ffill()
DataFrame Info - VIX¶
In [7]:
df_info(vix)
The columns, shape, and data types are: <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 8925 entries, 1990-01-02 to 2025-06-09 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Close 8925 non-null float64 1 High 8925 non-null float64 2 Low 8925 non-null float64 3 Open 8925 non-null float64 dtypes: float64(4) memory usage: 348.6 KB None The first 5 rows are:
Close | High | Low | Open | |
---|---|---|---|---|
Date | ||||
1990-01-02 | 17.24 | 17.24 | 17.24 | 17.24 |
1990-01-03 | 18.19 | 18.19 | 18.19 | 18.19 |
1990-01-04 | 19.22 | 19.22 | 19.22 | 19.22 |
1990-01-05 | 20.11 | 20.11 | 20.11 | 20.11 |
1990-01-08 | 20.26 | 20.26 | 20.26 | 20.26 |
The last 5 rows are:
Close | High | Low | Open | |
---|---|---|---|---|
Date | ||||
2025-06-03 | 17.69 | 19.21 | 17.64 | 18.83 |
2025-06-04 | 17.61 | 18.07 | 17.41 | 17.68 |
2025-06-05 | 18.48 | 18.80 | 17.08 | 17.68 |
2025-06-06 | 16.77 | 18.35 | 16.65 | 18.16 |
2025-06-09 | 17.16 | 17.72 | 16.82 | 17.69 |
In [8]:
# Copy this <!-- INSERT_01_VIX_DF_Info_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="01_VIX_DF_Info.md", content=df_info_markdown(vix))
✅ Exported and tracked: 01_VIX_DF_Info.md
Statistics - VIX¶
In [9]:
vix_stats = vix.describe()
num_std = [-1, 0, 1, 2, 3, 4, 5]
for num in num_std:
vix_stats.loc[f"mean + {num} std"] = {
'Open': vix_stats.loc['mean']['Open'] + num * vix_stats.loc['std']['Open'],
'High': vix_stats.loc['mean']['High'] + num * vix_stats.loc['std']['High'],
'Low': vix_stats.loc['mean']['Low'] + num * vix_stats.loc['std']['Low'],
'Close': vix_stats.loc['mean']['Close'] + num * vix_stats.loc['std']['Close'],
}
display(vix_stats)
Close | High | Low | Open | |
---|---|---|---|---|
count | 8925.00 | 8925.00 | 8925.00 | 8925.00 |
mean | 19.49 | 20.41 | 18.82 | 19.59 |
std | 7.84 | 8.39 | 7.39 | 7.91 |
min | 9.14 | 9.31 | 8.56 | 9.01 |
25% | 13.88 | 14.53 | 13.41 | 13.93 |
50% | 17.66 | 18.39 | 17.08 | 17.70 |
75% | 22.84 | 23.84 | 22.15 | 22.98 |
max | 82.69 | 89.53 | 72.76 | 82.69 |
mean + -1 std | 11.66 | 12.01 | 11.43 | 11.68 |
mean + 0 std | 19.49 | 20.41 | 18.82 | 19.59 |
mean + 1 std | 27.33 | 28.80 | 26.21 | 27.50 |
mean + 2 std | 35.16 | 37.19 | 33.60 | 35.41 |
mean + 3 std | 43.00 | 45.59 | 40.99 | 43.32 |
mean + 4 std | 50.84 | 53.98 | 48.38 | 51.23 |
mean + 5 std | 58.67 | 62.38 | 55.77 | 59.14 |
In [10]:
# Copy this <!-- INSERT_01_VIX_Stats_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="01_VIX_Stats.md", content=vix_stats.to_markdown(floatfmt=".2f"))
✅ Exported and tracked: 01_VIX_Stats.md
In [11]:
# Group by year and calculate mean and std for OHLC
vix_stats_by_year = vix.groupby(vix.index.year)[["Open", "High", "Low", "Close"]].agg(["mean", "std" ,"min", "max"])
# Flatten the column MultiIndex
vix_stats_by_year.columns = ['_'.join(col).strip() for col in vix_stats_by_year.columns.values]
vix_stats_by_year.index.name = "Year"
display(vix_stats_by_year)
Open_mean | Open_std | Open_min | Open_max | High_mean | High_std | High_min | High_max | Low_mean | Low_std | Low_min | Low_max | Close_mean | Close_std | Close_min | Close_max | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Year | ||||||||||||||||
1990 | 23.06 | 4.74 | 14.72 | 36.47 | 23.06 | 4.74 | 14.72 | 36.47 | 23.06 | 4.74 | 14.72 | 36.47 | 23.06 | 4.74 | 14.72 | 36.47 |
1991 | 18.38 | 3.68 | 13.95 | 36.20 | 18.38 | 3.68 | 13.95 | 36.20 | 18.38 | 3.68 | 13.95 | 36.20 | 18.38 | 3.68 | 13.95 | 36.20 |
1992 | 15.23 | 2.26 | 10.29 | 20.67 | 16.03 | 2.19 | 11.90 | 25.13 | 14.85 | 2.14 | 10.29 | 19.67 | 15.45 | 2.12 | 11.51 | 21.02 |
1993 | 12.70 | 1.37 | 9.18 | 16.20 | 13.34 | 1.40 | 9.55 | 18.31 | 12.25 | 1.28 | 8.89 | 15.77 | 12.69 | 1.33 | 9.31 | 17.30 |
1994 | 13.79 | 2.06 | 9.86 | 23.61 | 14.58 | 2.28 | 10.31 | 28.30 | 13.38 | 1.99 | 9.59 | 23.61 | 13.93 | 2.07 | 9.94 | 23.87 |
1995 | 12.27 | 1.03 | 10.29 | 15.79 | 12.93 | 1.07 | 10.95 | 16.99 | 11.96 | 0.98 | 10.06 | 14.97 | 12.39 | 0.97 | 10.36 | 15.74 |
1996 | 16.31 | 1.92 | 11.24 | 23.90 | 16.99 | 2.12 | 12.29 | 27.05 | 15.94 | 1.82 | 11.11 | 21.43 | 16.44 | 1.94 | 12.00 | 21.99 |
1997 | 22.43 | 4.33 | 16.67 | 45.69 | 23.11 | 4.56 | 18.02 | 48.64 | 21.85 | 3.98 | 16.36 | 36.43 | 22.38 | 4.14 | 17.09 | 38.20 |
1998 | 25.68 | 6.96 | 16.42 | 47.95 | 26.61 | 7.36 | 16.50 | 49.53 | 24.89 | 6.58 | 16.10 | 45.58 | 25.60 | 6.86 | 16.23 | 45.74 |
1999 | 24.39 | 2.90 | 18.05 | 32.62 | 25.20 | 3.01 | 18.48 | 33.66 | 23.75 | 2.76 | 17.07 | 31.13 | 24.37 | 2.88 | 17.42 | 32.98 |
2000 | 23.41 | 3.43 | 16.81 | 33.70 | 24.10 | 3.66 | 17.06 | 34.31 | 22.75 | 3.19 | 16.28 | 30.56 | 23.32 | 3.41 | 16.53 | 33.49 |
2001 | 26.04 | 4.98 | 19.21 | 48.93 | 26.64 | 5.19 | 19.37 | 49.35 | 25.22 | 4.61 | 18.74 | 42.66 | 25.75 | 4.78 | 18.76 | 43.74 |
2002 | 27.53 | 7.03 | 17.23 | 48.17 | 28.28 | 7.25 | 17.51 | 48.46 | 26.60 | 6.64 | 17.02 | 42.05 | 27.29 | 6.91 | 17.40 | 45.08 |
2003 | 22.21 | 5.31 | 15.59 | 35.21 | 22.61 | 5.35 | 16.19 | 35.66 | 21.64 | 5.18 | 14.66 | 33.99 | 21.98 | 5.24 | 15.58 | 34.69 |
2004 | 15.59 | 1.93 | 11.41 | 21.06 | 16.05 | 2.02 | 11.64 | 22.67 | 15.05 | 1.79 | 11.14 | 20.61 | 15.48 | 1.92 | 11.23 | 21.58 |
2005 | 12.84 | 1.44 | 10.23 | 18.33 | 13.28 | 1.59 | 10.48 | 18.59 | 12.39 | 1.32 | 9.88 | 16.41 | 12.81 | 1.47 | 10.23 | 17.74 |
2006 | 12.90 | 2.18 | 9.68 | 23.45 | 13.33 | 2.46 | 10.06 | 23.81 | 12.38 | 1.96 | 9.39 | 21.45 | 12.81 | 2.25 | 9.90 | 23.81 |
2007 | 17.59 | 5.36 | 9.99 | 32.68 | 18.44 | 5.76 | 10.26 | 37.50 | 16.75 | 4.95 | 9.70 | 30.44 | 17.54 | 5.36 | 9.89 | 31.09 |
2008 | 32.83 | 16.41 | 16.30 | 80.74 | 34.57 | 17.83 | 17.84 | 89.53 | 30.96 | 14.96 | 15.82 | 72.76 | 32.69 | 16.38 | 16.30 | 80.86 |
2009 | 31.75 | 9.20 | 19.54 | 52.65 | 32.78 | 9.61 | 19.67 | 57.36 | 30.50 | 8.63 | 19.25 | 49.27 | 31.48 | 9.08 | 19.47 | 56.65 |
2010 | 22.73 | 5.29 | 15.44 | 47.66 | 23.69 | 5.82 | 16.00 | 48.20 | 21.69 | 4.61 | 15.23 | 40.30 | 22.55 | 5.27 | 15.45 | 45.79 |
2011 | 24.27 | 8.17 | 14.31 | 46.18 | 25.40 | 8.78 | 14.99 | 48.00 | 23.15 | 7.59 | 14.27 | 41.51 | 24.20 | 8.14 | 14.62 | 48.00 |
2012 | 17.93 | 2.60 | 13.68 | 26.35 | 18.59 | 2.72 | 14.08 | 27.73 | 17.21 | 2.37 | 13.30 | 25.72 | 17.80 | 2.54 | 13.45 | 26.66 |
2013 | 14.29 | 1.67 | 11.52 | 20.87 | 14.82 | 1.88 | 11.75 | 21.91 | 13.80 | 1.51 | 11.05 | 19.04 | 14.23 | 1.74 | 11.30 | 20.49 |
2014 | 14.23 | 2.65 | 10.40 | 29.26 | 14.95 | 3.02 | 10.76 | 31.06 | 13.61 | 2.21 | 10.28 | 24.64 | 14.17 | 2.62 | 10.32 | 25.27 |
2015 | 16.71 | 3.99 | 11.77 | 31.91 | 17.79 | 5.03 | 12.22 | 53.29 | 15.85 | 3.65 | 10.88 | 29.91 | 16.67 | 4.34 | 11.95 | 40.74 |
2016 | 16.01 | 4.05 | 11.32 | 29.01 | 16.85 | 4.40 | 11.49 | 32.09 | 15.16 | 3.66 | 10.93 | 26.67 | 15.83 | 3.97 | 11.27 | 28.14 |
2017 | 11.14 | 1.34 | 9.23 | 16.19 | 11.72 | 1.54 | 9.52 | 17.28 | 10.64 | 1.16 | 8.56 | 14.97 | 11.09 | 1.36 | 9.14 | 16.04 |
2018 | 16.63 | 5.01 | 9.01 | 37.32 | 18.03 | 6.12 | 9.31 | 50.30 | 15.53 | 4.25 | 8.92 | 29.66 | 16.64 | 5.09 | 9.15 | 37.32 |
2019 | 15.57 | 2.74 | 11.55 | 27.54 | 16.41 | 3.06 | 11.79 | 28.53 | 14.76 | 2.38 | 11.03 | 24.05 | 15.39 | 2.61 | 11.54 | 25.45 |
2020 | 29.54 | 12.45 | 12.20 | 82.69 | 31.46 | 13.89 | 12.42 | 85.47 | 27.51 | 10.85 | 11.75 | 70.37 | 29.25 | 12.34 | 12.10 | 82.69 |
2021 | 19.83 | 3.47 | 15.02 | 35.16 | 21.12 | 4.22 | 15.54 | 37.51 | 18.65 | 2.93 | 14.10 | 29.24 | 19.66 | 3.62 | 15.01 | 37.21 |
2022 | 25.98 | 4.30 | 16.57 | 37.50 | 27.25 | 4.59 | 17.81 | 38.94 | 24.69 | 3.91 | 16.34 | 33.11 | 25.62 | 4.22 | 16.60 | 36.45 |
2023 | 17.12 | 3.17 | 11.96 | 27.77 | 17.83 | 3.58 | 12.46 | 30.81 | 16.36 | 2.89 | 11.81 | 24.00 | 16.87 | 3.14 | 12.07 | 26.52 |
2024 | 15.69 | 3.14 | 11.53 | 33.71 | 16.65 | 4.73 | 12.23 | 65.73 | 14.92 | 2.58 | 10.62 | 24.02 | 15.61 | 3.36 | 11.86 | 38.57 |
2025 | 21.84 | 7.25 | 14.89 | 60.13 | 23.53 | 8.85 | 15.16 | 60.13 | 20.30 | 5.34 | 14.58 | 38.58 | 21.52 | 6.87 | 14.77 | 52.33 |
In [12]:
# Copy this <!-- INSERT_01_VIX_Stats_By_Year_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="01_VIX_Stats_By_Year.md", content=vix_stats_by_year.to_markdown(floatfmt=".2f"))
✅ Exported and tracked: 01_VIX_Stats_By_Year.md
In [13]:
# Group by month and calculate mean and std for OHLC
vix_stats_by_month = vix.groupby(vix.index.month)[["Open", "High", "Low", "Close"]].agg(["mean", "std", "min", "max"])
# Flatten the column MultiIndex
vix_stats_by_month.columns = ['_'.join(col).strip() for col in vix_stats_by_month.columns.values]
vix_stats_by_month.index.name = "Month"
display(vix_stats_by_month)
Open_mean | Open_std | Open_min | Open_max | High_mean | High_std | High_min | High_max | Low_mean | Low_std | Low_min | Low_max | Close_mean | Close_std | Close_min | Close_max | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Month | ||||||||||||||||
1 | 19.34 | 7.21 | 9.01 | 51.52 | 20.13 | 7.58 | 9.31 | 57.36 | 18.60 | 6.87 | 8.92 | 49.27 | 19.22 | 7.17 | 9.15 | 56.65 |
2 | 19.67 | 7.22 | 10.19 | 52.50 | 20.51 | 7.65 | 10.26 | 53.16 | 18.90 | 6.81 | 9.70 | 48.97 | 19.58 | 7.13 | 10.02 | 52.62 |
3 | 20.47 | 9.63 | 10.59 | 82.69 | 21.39 | 10.49 | 11.24 | 85.47 | 19.54 | 8.65 | 10.53 | 70.37 | 20.35 | 9.56 | 10.74 | 82.69 |
4 | 19.43 | 7.48 | 10.39 | 60.13 | 20.24 | 7.93 | 10.89 | 60.59 | 18.65 | 6.88 | 10.22 | 52.76 | 19.29 | 7.28 | 10.36 | 57.06 |
5 | 18.60 | 6.04 | 9.75 | 47.66 | 19.40 | 6.43 | 10.14 | 48.20 | 17.89 | 5.63 | 9.56 | 40.30 | 18.51 | 5.96 | 9.77 | 45.79 |
6 | 18.45 | 5.80 | 9.79 | 44.09 | 19.15 | 6.07 | 10.28 | 44.44 | 17.73 | 5.44 | 9.37 | 34.97 | 18.34 | 5.72 | 9.75 | 40.79 |
7 | 17.87 | 5.75 | 9.18 | 48.17 | 18.58 | 5.98 | 9.52 | 48.46 | 17.24 | 5.48 | 8.84 | 42.05 | 17.80 | 5.67 | 9.36 | 44.92 |
8 | 19.17 | 6.74 | 10.04 | 45.34 | 20.12 | 7.45 | 10.32 | 65.73 | 18.44 | 6.38 | 9.52 | 41.77 | 19.18 | 6.87 | 9.93 | 48.00 |
9 | 20.51 | 8.32 | 9.59 | 48.93 | 21.35 | 8.64 | 9.83 | 49.35 | 19.74 | 7.90 | 9.36 | 43.74 | 20.43 | 8.20 | 9.51 | 46.72 |
10 | 21.83 | 10.28 | 9.23 | 79.13 | 22.83 | 11.10 | 9.62 | 89.53 | 20.93 | 9.51 | 9.11 | 67.80 | 21.75 | 10.24 | 9.19 | 80.06 |
11 | 20.34 | 9.65 | 9.31 | 80.74 | 21.04 | 10.03 | 9.74 | 81.48 | 19.55 | 9.02 | 8.56 | 72.76 | 20.16 | 9.52 | 9.14 | 80.86 |
12 | 19.34 | 8.26 | 9.36 | 66.68 | 20.09 | 8.53 | 9.55 | 68.60 | 18.63 | 7.88 | 8.89 | 62.31 | 19.29 | 8.16 | 9.31 | 68.51 |
In [14]:
# Copy this <!-- INSERT_01_VIX_Stats_By_Month_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="01_VIX_Stats_By_Month.md", content=vix_stats_by_month.to_markdown(floatfmt=".2f"))
✅ Exported and tracked: 01_VIX_Stats_By_Month.md
Deciles - VIX¶
In [15]:
vix_deciles = vix.quantile(np.arange(0, 1.1, 0.1))
display(vix_deciles)
Close | High | Low | Open | |
---|---|---|---|---|
0.00 | 9.14 | 9.31 | 8.56 | 9.01 |
0.10 | 12.12 | 12.63 | 11.72 | 12.13 |
0.20 | 13.26 | 13.88 | 12.85 | 13.31 |
0.30 | 14.61 | 15.29 | 14.08 | 14.68 |
0.40 | 16.10 | 16.76 | 15.56 | 16.13 |
0.50 | 17.66 | 18.39 | 17.08 | 17.70 |
0.60 | 19.55 | 20.39 | 19.00 | 19.69 |
0.70 | 21.64 | 22.65 | 20.99 | 21.79 |
0.80 | 24.31 | 25.36 | 23.50 | 24.38 |
0.90 | 28.70 | 30.00 | 27.78 | 28.86 |
1.00 | 82.69 | 89.53 | 72.76 | 82.69 |
In [16]:
# Copy this <!-- INSERT_01_VIX_Deciles_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="01_VIX_Deciles.md", content=vix_deciles.to_markdown(floatfmt=".2f"))
✅ Exported and tracked: 01_VIX_Deciles.md
Plots - VIX¶
Histogram Distribution - VIX¶
In [17]:
# Plotting
plt.figure(figsize=(12, 6), facecolor="#F5F5F5")
# Histogram
plt.hist([vix['High']], label=['High'], bins=200, edgecolor='black', color='steelblue')
# Plot a vertical line at the mean, mean + 1 std, and mean + 2 std
plt.axvline(vix_stats.loc['mean + -1 std']['High'], color='brown', linestyle='dashed', linewidth=1, label=f'High Mean - 1 std: {vix_stats.loc['mean + -1 std']['High']:.2f}')
plt.axvline(vix_stats.loc['mean']['High'], color='red', linestyle='dashed', linewidth=1, label=f'High Mean: {vix_stats.loc['mean']['High']:.2f}')
plt.axvline(vix_stats.loc['mean + 1 std']['High'], color='green', linestyle='dashed', linewidth=1, label=f'High Mean + 1 std: {vix_stats.loc['mean + 1 std']['High']:.2f}')
plt.axvline(vix_stats.loc['mean + 2 std']['High'], color='orange', linestyle='dashed', linewidth=1, label=f'High Mean + 2 std: {vix_stats.loc['mean + 2 std']['High']:.2f}')
plt.axvline(vix_stats.loc['mean + 3 std']['High'], color='black', linestyle='dashed', linewidth=1, label=f'High Mean + 3 std: {vix_stats.loc['mean + 3 std']['High']:.2f}')
plt.axvline(vix_stats.loc['mean + 4 std']['High'], color='yellow', linestyle='dashed', linewidth=1, label=f'High Mean + 4 std: {vix_stats.loc['mean + 4 std']['High']:.2f}')
# Set X axis
x_tick_spacing = 5 # Specify the interval for y-axis ticks
plt.gca().xaxis.set_major_locator(MultipleLocator(x_tick_spacing))
plt.xlabel("VIX", fontsize=10)
plt.xticks(rotation=0, fontsize=8)
# Set Y axis
y_tick_spacing = 25 # Specify the interval for y-axis ticks
plt.gca().yaxis.set_major_locator(MultipleLocator(y_tick_spacing))
plt.ylabel("# Of Datapoints", fontsize=10)
plt.yticks(fontsize=8)
# Set title, layout, grid, and legend
plt.title("CBOE Volatility Index (VIX) Histogram (200 Bins)", fontsize=12)
plt.tight_layout()
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend(fontsize=9)
# Save figure and display plot
plt.savefig("01_Histogram+Mean+SD.png", dpi=300, bbox_inches="tight")
plt.show()
Historical Data - VIX¶
In [18]:
plot_price(
price_df=vix,
plot_start_date=None,
plot_end_date="2009-12-31",
plot_columns=["High", "Low"],
title="CBOE Volatility Index (VIX), 1990 - 2009",
x_label="Date",
x_format="Year",
y_label="VIX",
y_format="Decimal",
y_tick_spacing=5,
grid=True,
legend=True,
export_plot=True,
plot_file_name="01_VIX_Plot_1990-2009",
)
In [19]:
plot_price(
price_df=vix,
plot_start_date="2010-01-01",
plot_end_date=None,
plot_columns=["High", "Low"],
title="CBOE Volatility Index (VIX), 2010 - Present",
x_label="Date",
x_format="Year",
y_label="VIX",
y_format="Decimal",
y_tick_spacing=5,
grid=True,
legend=True,
export_plot=True,
plot_file_name="01_VIX_Plot_2010-Present",
)
Stats By Year - VIX¶
In [20]:
plot_stats(
stats_df=vix_stats_by_year,
plot_columns=["Open_mean", "High_mean", "Low_mean", "Close_mean"],
title="VIX Mean OHLC By Year",
x_label="Year",
x_rotation=45,
x_tick_spacing=1,
y_label="Price",
y_tick_spacing=1,
grid=True,
legend=True,
export_plot=True,
plot_file_name="01_VIX_Stats_By_Year"
)
Stats By Month - VIX¶
In [21]:
plot_stats(
stats_df=vix_stats_by_month,
plot_columns=["Open_mean", "High_mean", "Low_mean", "Close_mean"],
title="VIX Mean OHLC By Month",
x_label="Month",
x_rotation=0,
x_tick_spacing=1,
y_label="Price",
y_tick_spacing=1,
grid=True,
legend=True,
export_plot=True,
plot_file_name="01_VIX_Stats_By_Month"
)
Data Overview (VVIX)¶
Acquire CBOE VVIX Data¶
In [22]:
yf_pull_data(
base_directory=DATA_DIR,
ticker="^VVIX",
source="Yahoo_Finance",
asset_class="Indices",
excel_export=True,
pickle_export=True,
output_confirmation=True,
)
[*********************100%***********************] 1 of 1 completed
The first and last date of data for ^VVIX is:
Close | High | Low | Open | Volume | |
---|---|---|---|---|---|
Date | |||||
2007-01-03 | 87.63 | 87.63 | 87.63 | 87.63 | 0 |
Close | High | Low | Open | Volume | |
---|---|---|---|---|---|
Date | |||||
2025-06-09 | 88.97 | 91.84 | 88.59 | 91.58 | 0 |
Yahoo Finance data complete for ^VVIX --------------------
Out[22]:
Close | High | Low | Open | Volume | |
---|---|---|---|---|---|
Date | |||||
2007-01-03 | 87.63 | 87.63 | 87.63 | 87.63 | 0 |
2007-01-04 | 88.19 | 88.19 | 88.19 | 88.19 | 0 |
2007-01-05 | 90.17 | 90.17 | 90.17 | 90.17 | 0 |
2007-01-08 | 92.04 | 92.04 | 92.04 | 92.04 | 0 |
2007-01-09 | 92.76 | 92.76 | 92.76 | 92.76 | 0 |
... | ... | ... | ... | ... | ... |
2025-06-03 | 90.89 | 94.38 | 90.06 | 91.58 | 0 |
2025-06-04 | 90.79 | 94.56 | 90.42 | 91.16 | 0 |
2025-06-05 | 93.20 | 94.53 | 88.92 | 90.26 | 0 |
2025-06-06 | 89.45 | 91.20 | 88.32 | 91.20 | 0 |
2025-06-09 | 88.97 | 91.84 | 88.59 | 91.58 | 0 |
4629 rows × 5 columns
Load Data - VVIX¶
In [23]:
# Set decimal places
pandas_set_decimal_places(2)
# VVIX
vvix = load_data(
base_directory=DATA_DIR,
ticker="^VVIX",
source="Yahoo_Finance",
asset_class="Indices",
timeframe="Daily",
)
# Set 'Date' column as datetime
vvix['Date'] = pd.to_datetime(vvix['Date'])
# Drop 'Volume'
vvix.drop(columns = {'Volume'}, inplace = True)
# Set Date as index
vvix.set_index('Date', inplace = True)
# Check to see if there are any NaN values
vvix[vvix['High'].isna()]
# Forward fill to clean up missing data
vvix['High'] = vvix['High'].ffill()
DataFrame Info - VVIX¶
In [24]:
df_info(vvix)
The columns, shape, and data types are: <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 4629 entries, 2007-01-03 to 2025-06-09 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Close 4629 non-null float64 1 High 4629 non-null float64 2 Low 4629 non-null float64 3 Open 4629 non-null float64 dtypes: float64(4) memory usage: 180.8 KB None The first 5 rows are:
Close | High | Low | Open | |
---|---|---|---|---|
Date | ||||
2007-01-03 | 87.63 | 87.63 | 87.63 | 87.63 |
2007-01-04 | 88.19 | 88.19 | 88.19 | 88.19 |
2007-01-05 | 90.17 | 90.17 | 90.17 | 90.17 |
2007-01-08 | 92.04 | 92.04 | 92.04 | 92.04 |
2007-01-09 | 92.76 | 92.76 | 92.76 | 92.76 |
The last 5 rows are:
Close | High | Low | Open | |
---|---|---|---|---|
Date | ||||
2025-06-03 | 90.89 | 94.38 | 90.06 | 91.58 |
2025-06-04 | 90.79 | 94.56 | 90.42 | 91.16 |
2025-06-05 | 93.20 | 94.53 | 88.92 | 90.26 |
2025-06-06 | 89.45 | 91.20 | 88.32 | 91.20 |
2025-06-09 | 88.97 | 91.84 | 88.59 | 91.58 |
In [25]:
# Copy this <!-- INSERT_02_VVIX_DF_Info_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="02_VVIX_DF_Info.md", content=df_info_markdown(vix))
✅ Exported and tracked: 02_VVIX_DF_Info.md
Statistics - VVIX¶
In [26]:
vvix_stats = vvix.describe()
num_std = [-1, 0, 1, 2, 3, 4, 5]
for num in num_std:
vvix_stats.loc[f"mean + {num} std"] = {
'Open': vvix_stats.loc['mean']['Open'] + num * vvix_stats.loc['std']['Open'],
'High': vvix_stats.loc['mean']['High'] + num * vvix_stats.loc['std']['High'],
'Low': vvix_stats.loc['mean']['Low'] + num * vvix_stats.loc['std']['Low'],
'Close': vvix_stats.loc['mean']['Close'] + num * vvix_stats.loc['std']['Close'],
}
display(vvix_stats)
Close | High | Low | Open | |
---|---|---|---|---|
count | 4629.00 | 4629.00 | 4629.00 | 4629.00 |
mean | 93.50 | 95.55 | 91.94 | 93.75 |
std | 16.44 | 18.04 | 15.09 | 16.49 |
min | 59.74 | 59.74 | 59.31 | 59.31 |
25% | 82.34 | 83.46 | 81.48 | 82.55 |
50% | 90.54 | 92.26 | 89.37 | 90.86 |
75% | 102.20 | 105.01 | 99.93 | 102.55 |
max | 207.59 | 212.22 | 187.27 | 212.22 |
mean + -1 std | 77.06 | 77.50 | 76.85 | 77.26 |
mean + 0 std | 93.50 | 95.55 | 91.94 | 93.75 |
mean + 1 std | 109.94 | 113.59 | 107.04 | 110.25 |
mean + 2 std | 126.38 | 131.64 | 122.13 | 126.74 |
mean + 3 std | 142.82 | 149.68 | 137.22 | 143.23 |
mean + 4 std | 159.26 | 167.73 | 152.32 | 159.72 |
mean + 5 std | 175.69 | 185.77 | 167.41 | 176.21 |
In [27]:
# Copy this <!-- INSERT_02_VVIX_Stats_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="02_VVIX_Stats.md", content=vvix_stats.to_markdown(floatfmt=".2f"))
✅ Exported and tracked: 02_VVIX_Stats.md
In [28]:
# Group by year and calculate mean and std for OHLC
vvix_stats_by_year = vvix.groupby(vvix.index.year)[["Open", "High", "Low", "Close"]].agg(["mean", "std", "min", "max"])
# Flatten the column MultiIndex
vvix_stats_by_year.columns = ['_'.join(col).strip() for col in vvix_stats_by_year.columns.values]
vvix_stats_by_year.index.name = "Year"
display(vvix_stats_by_year)
Open_mean | Open_std | Open_min | Open_max | High_mean | High_std | High_min | High_max | Low_mean | Low_std | Low_min | Low_max | Close_mean | Close_std | Close_min | Close_max | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Year | ||||||||||||||||
2007 | 87.68 | 13.31 | 63.52 | 142.99 | 87.68 | 13.31 | 63.52 | 142.99 | 87.68 | 13.31 | 63.52 | 142.99 | 87.68 | 13.31 | 63.52 | 142.99 |
2008 | 81.85 | 15.60 | 59.74 | 134.87 | 81.85 | 15.60 | 59.74 | 134.87 | 81.85 | 15.60 | 59.74 | 134.87 | 81.85 | 15.60 | 59.74 | 134.87 |
2009 | 79.78 | 8.63 | 64.95 | 104.02 | 79.78 | 8.63 | 64.95 | 104.02 | 79.78 | 8.63 | 64.95 | 104.02 | 79.78 | 8.63 | 64.95 | 104.02 |
2010 | 88.36 | 13.07 | 64.87 | 145.12 | 88.36 | 13.07 | 64.87 | 145.12 | 88.36 | 13.07 | 64.87 | 145.12 | 88.36 | 13.07 | 64.87 | 145.12 |
2011 | 92.94 | 10.21 | 75.94 | 134.63 | 92.94 | 10.21 | 75.94 | 134.63 | 92.94 | 10.21 | 75.94 | 134.63 | 92.94 | 10.21 | 75.94 | 134.63 |
2012 | 94.84 | 8.38 | 78.42 | 117.44 | 94.84 | 8.38 | 78.42 | 117.44 | 94.84 | 8.38 | 78.42 | 117.44 | 94.84 | 8.38 | 78.42 | 117.44 |
2013 | 80.52 | 8.97 | 62.71 | 111.43 | 80.52 | 8.97 | 62.71 | 111.43 | 80.52 | 8.97 | 62.71 | 111.43 | 80.52 | 8.97 | 62.71 | 111.43 |
2014 | 83.01 | 14.33 | 61.76 | 138.60 | 83.01 | 14.33 | 61.76 | 138.60 | 83.01 | 14.33 | 61.76 | 138.60 | 83.01 | 14.33 | 61.76 | 138.60 |
2015 | 95.44 | 15.59 | 73.07 | 212.22 | 98.47 | 16.39 | 76.41 | 212.22 | 92.15 | 13.35 | 72.20 | 148.68 | 94.82 | 14.75 | 73.18 | 168.75 |
2016 | 93.36 | 10.02 | 77.96 | 131.95 | 95.82 | 10.86 | 78.86 | 132.42 | 90.54 | 8.99 | 76.17 | 115.15 | 92.80 | 10.07 | 76.17 | 125.13 |
2017 | 90.50 | 8.65 | 75.09 | 134.98 | 92.94 | 9.64 | 77.34 | 135.32 | 87.85 | 7.78 | 71.75 | 117.29 | 90.01 | 8.80 | 75.64 | 135.32 |
2018 | 102.60 | 13.22 | 83.70 | 176.72 | 106.27 | 16.26 | 85.00 | 203.73 | 99.17 | 11.31 | 82.60 | 165.35 | 102.26 | 14.04 | 83.21 | 180.61 |
2019 | 91.28 | 8.43 | 75.58 | 112.75 | 93.61 | 8.98 | 75.95 | 117.63 | 88.90 | 7.86 | 74.36 | 111.48 | 91.03 | 8.36 | 74.98 | 114.40 |
2020 | 118.64 | 19.32 | 88.39 | 203.03 | 121.91 | 20.88 | 88.54 | 209.76 | 115.05 | 17.37 | 85.31 | 187.27 | 118.36 | 19.39 | 86.87 | 207.59 |
2021 | 115.51 | 9.37 | 96.09 | 151.35 | 119.29 | 11.70 | 98.36 | 168.78 | 111.99 | 8.14 | 95.92 | 144.19 | 115.32 | 10.20 | 97.09 | 157.69 |
2022 | 102.58 | 18.01 | 76.48 | 161.09 | 105.32 | 19.16 | 77.93 | 172.82 | 99.17 | 16.81 | 76.13 | 153.26 | 101.81 | 17.81 | 77.05 | 154.38 |
2023 | 90.95 | 8.64 | 74.43 | 127.73 | 93.72 | 9.98 | 75.31 | 137.65 | 88.01 | 7.37 | 72.27 | 119.64 | 90.34 | 8.38 | 73.88 | 124.75 |
2024 | 92.88 | 15.06 | 59.31 | 169.68 | 97.32 | 18.33 | 74.79 | 192.49 | 89.51 | 13.16 | 59.31 | 137.05 | 92.81 | 15.60 | 73.26 | 173.32 |
2025 | 106.30 | 15.89 | 83.19 | 186.33 | 111.30 | 18.68 | 85.82 | 189.03 | 101.64 | 12.37 | 81.73 | 146.51 | 105.26 | 15.14 | 81.89 | 170.92 |
In [29]:
# Copy this <!-- INSERT_02_VVIX_Stats_By_Year_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="02_VVIX_Stats_By_Year.md", content=vvix_stats_by_year.to_markdown(floatfmt=".2f"))
✅ Exported and tracked: 02_VVIX_Stats_By_Year.md
In [30]:
# Group by month and calculate mean and std for OHLC
vvix_stats_by_month = vvix.groupby(vvix.index.month)[["Open", "High", "Low", "Close"]].agg(["mean", "std", "min", "max"])
# Flatten the column MultiIndex
vvix_stats_by_month.columns = ['_'.join(col).strip() for col in vvix_stats_by_month.columns.values]
vvix_stats_by_month.index.name = "Year"
display(vvix_stats_by_month)
Open_mean | Open_std | Open_min | Open_max | High_mean | High_std | High_min | High_max | Low_mean | Low_std | Low_min | Low_max | Close_mean | Close_std | Close_min | Close_max | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Year | ||||||||||||||||
1 | 92.46 | 15.63 | 64.87 | 161.09 | 94.37 | 17.63 | 64.87 | 172.82 | 90.69 | 14.23 | 64.87 | 153.26 | 92.23 | 15.78 | 64.87 | 157.69 |
2 | 93.49 | 18.24 | 65.47 | 176.72 | 95.39 | 20.70 | 65.47 | 203.73 | 91.39 | 16.43 | 65.47 | 165.35 | 93.13 | 18.58 | 65.47 | 180.61 |
3 | 95.30 | 21.66 | 66.97 | 203.03 | 97.38 | 23.56 | 66.97 | 209.76 | 92.94 | 19.51 | 66.97 | 187.27 | 94.89 | 21.59 | 66.97 | 207.59 |
4 | 92.18 | 19.03 | 59.74 | 186.33 | 94.01 | 20.57 | 59.74 | 189.03 | 90.30 | 17.21 | 59.74 | 152.01 | 91.88 | 18.60 | 59.74 | 170.92 |
5 | 92.25 | 16.93 | 61.76 | 145.18 | 93.95 | 17.99 | 61.76 | 151.50 | 90.54 | 16.14 | 61.76 | 145.12 | 91.79 | 16.79 | 61.76 | 146.28 |
6 | 92.91 | 14.96 | 63.52 | 155.48 | 94.44 | 16.21 | 63.52 | 172.21 | 91.30 | 13.92 | 63.52 | 140.15 | 92.72 | 14.93 | 63.52 | 151.60 |
7 | 89.97 | 13.16 | 67.21 | 138.42 | 91.46 | 14.23 | 67.21 | 149.60 | 88.48 | 12.26 | 67.21 | 133.82 | 89.84 | 13.12 | 67.21 | 139.54 |
8 | 96.83 | 16.94 | 68.05 | 212.22 | 98.89 | 18.72 | 68.05 | 212.22 | 94.68 | 14.86 | 68.05 | 148.68 | 96.61 | 16.63 | 68.05 | 173.32 |
9 | 94.71 | 14.03 | 67.94 | 135.17 | 96.50 | 15.52 | 67.94 | 146.31 | 92.86 | 12.50 | 67.94 | 128.46 | 94.40 | 13.78 | 67.94 | 138.93 |
10 | 97.74 | 14.01 | 64.97 | 149.60 | 99.43 | 15.11 | 64.97 | 154.99 | 96.14 | 13.35 | 64.97 | 144.55 | 97.52 | 14.15 | 64.97 | 152.01 |
11 | 93.53 | 14.17 | 63.77 | 142.68 | 95.07 | 15.36 | 63.77 | 161.76 | 91.98 | 13.39 | 63.77 | 140.44 | 93.28 | 14.24 | 63.77 | 149.74 |
12 | 93.35 | 15.03 | 59.31 | 151.35 | 95.33 | 16.63 | 62.71 | 168.37 | 91.78 | 13.70 | 59.31 | 144.19 | 93.46 | 15.07 | 62.71 | 156.10 |
In [31]:
# Copy this <!-- INSERT_02_VVIX_Stats_By_Month_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="02_VVIX_Stats_By_Month.md", content=vvix_stats_by_month.to_markdown(floatfmt=".2f"))
✅ Exported and tracked: 02_VVIX_Stats_By_Month.md
Deciles - VVIX¶
In [32]:
vvix_deciles = vvix.quantile(np.arange(0, 1.1, 0.1))
display(vvix_deciles)
Close | High | Low | Open | |
---|---|---|---|---|
0.00 | 59.74 | 59.74 | 59.31 | 59.31 |
0.10 | 75.83 | 76.02 | 75.44 | 75.80 |
0.20 | 80.58 | 81.43 | 79.82 | 80.75 |
0.30 | 83.90 | 85.22 | 83.01 | 84.17 |
0.40 | 87.08 | 88.57 | 85.98 | 87.46 |
0.50 | 90.54 | 92.26 | 89.37 | 90.86 |
0.60 | 94.21 | 96.14 | 93.03 | 94.51 |
0.70 | 99.11 | 101.53 | 97.43 | 99.40 |
0.80 | 106.06 | 109.40 | 103.90 | 106.48 |
0.90 | 115.27 | 118.80 | 112.48 | 115.53 |
1.00 | 207.59 | 212.22 | 187.27 | 212.22 |
In [33]:
# Copy this <!-- INSERT_02_VVIX_Deciles_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="02_VVIX_Deciles.md", content=vvix_deciles.to_markdown(floatfmt=".2f"))
✅ Exported and tracked: 02_VVIX_Deciles.md
Plots - VVIX¶
Histogram Distribution - VVIX¶
In [34]:
# Plotting
plt.figure(figsize=(12, 6), facecolor="#F5F5F5")
# Histogram
plt.hist([vvix['High']], label=['High'], bins=200, edgecolor='black', color='steelblue')
# Plot a vertical line at the mean, mean + 1 std, and mean + 2 std
plt.axvline(vvix_stats.loc['mean + -1 std']['High'], color='brown', linestyle='dashed', linewidth=1, label=f'Mean - 1 std: {vvix_stats.loc['mean + -1 std']['High']:.2f}')
plt.axvline(vvix_stats.loc['mean']['High'], color='red', linestyle='dashed', linewidth=1, label=f'Mean: {vvix_stats.loc['mean']['High']:.2f}')
plt.axvline(vvix_stats.loc['mean + 1 std']['High'], color='green', linestyle='dashed', linewidth=1, label=f'Mean + 1 std: {vvix_stats.loc['mean + 1 std']['High']:.2f}')
plt.axvline(vvix_stats.loc['mean + 2 std']['High'], color='orange', linestyle='dashed', linewidth=1, label=f'Mean + 2 std: {vvix_stats.loc['mean + 2 std']['High']:.2f}')
plt.axvline(vvix_stats.loc['mean + 3 std']['High'], color='black', linestyle='dashed', linewidth=1, label=f'Mean + 3 std: {vvix_stats.loc['mean + 3 std']['High']:.2f}')
plt.axvline(vvix_stats.loc['mean + 4 std']['High'], color='yellow', linestyle='dashed', linewidth=1, label=f'Mean + 4 std: {vvix_stats.loc['mean + 4 std']['High']:.2f}')
# Set X axis
x_tick_spacing = 5 # Specify the interval for y-axis ticks
plt.gca().xaxis.set_major_locator(MultipleLocator(x_tick_spacing))
plt.xlabel("VVIX", fontsize=10)
plt.xticks(rotation=0, fontsize=8)
# Set Y axis
y_tick_spacing = 25 # Specify the interval for y-axis ticks
plt.gca().yaxis.set_major_locator(MultipleLocator(y_tick_spacing))
plt.ylabel("# Of Datapoints", fontsize=10)
plt.yticks(fontsize=8)
# Set title, layout, grid, and legend
plt.title("CBOE VVIX Histogram (200 Bins)", fontsize=12)
plt.tight_layout()
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend(fontsize=9)
# Save figure and display plot
plt.savefig("02_Histogram+Mean+SD.png", dpi=300, bbox_inches="tight")
plt.show()
Historical Data - VVIX¶
In [35]:
plot_price(
price_df=vvix,
plot_start_date=None,
plot_end_date="2016-12-31",
plot_columns=["High", "Low"],
title="CBOE VVIX, 2007 - 2016",
x_label="Date",
x_format="Year",
y_label="VIX",
y_format="Decimal",
y_tick_spacing=15,
grid=True,
legend=True,
export_plot=True,
plot_file_name="02_VVIX_Plot_2007-2016",
)
In [36]:
plot_price(
price_df=vvix,
plot_start_date="2017-01-01",
plot_end_date=None,
plot_columns=["High", "Low"],
title="CBOE VVIX, 2017 - Present",
x_label="Date",
x_format="Year",
y_label="VIX",
y_format="Decimal",
y_tick_spacing=15,
grid=True,
legend=True,
export_plot=True,
plot_file_name="02_VVIX_Plot_2017-Present",
)
Stats By Year - VVIX¶
In [37]:
plot_stats(
stats_df=vvix_stats_by_year,
plot_columns=["Open_mean", "High_mean", "Low_mean", "Close_mean"],
title="VVIX Mean OHLC By Year",
x_label="Year",
x_rotation=45,
x_tick_spacing=1,
y_label="Price",
y_tick_spacing=5,
grid=True,
legend=True,
export_plot=True,
plot_file_name="02_VVIX_Stats_By_Year"
)
Stats By Month - VVIX¶
In [38]:
plot_stats(
stats_df=vvix_stats_by_month,
plot_columns=["Open_mean", "High_mean", "Low_mean", "Close_mean"],
title="VVIX Mean OHLC By Month",
x_label="Month",
x_rotation=0,
x_tick_spacing=1,
y_label="Price",
y_tick_spacing=1,
grid=True,
legend=True,
export_plot=True,
plot_file_name="02_VVIX_Stats_By_Month"
)
In [39]:
# Plotting
plt.figure(figsize=(12, 6), facecolor="#F5F5F5")
# Plot data
plt.plot(vvix[vvix.index > '2023-12-31'].index, vvix[vvix.index > '2023-12-31']['High'], label='High', linestyle='-', color='steelblue', linewidth=1.5)
plt.plot(vvix[vvix.index > '2023-12-31'].index, vvix[vvix.index > '2023-12-31']['Low'], label='Low', linestyle='-', color='brown', linewidth=1.5)
# Set X axis
plt.gca().xaxis.set_major_locator(mdates.MonthLocator())
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
plt.xlabel("Date", fontsize=10)
plt.xticks(rotation=45, fontsize=8)
# Set Y axis
y_tick_spacing = 5 # Specify the interval for y-axis ticks
plt.gca().yaxis.set_major_locator(MultipleLocator(y_tick_spacing))
plt.ylabel("VVIX", fontsize=10)
plt.yticks(fontsize=8)
# Set title, layout, grid, and legend
plt.title("CBOE VVIX, 2024 - Present", fontsize=12)
plt.tight_layout()
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend(fontsize=9)
# Save figure and display plot
plt.savefig("02_VVIX_Plot_2024-Present.png", dpi=300, bbox_inches="tight")
plt.show()
In [40]:
# Plotting
plt.figure(figsize=(12, 6), facecolor="#F5F5F5")
# Plot data
plt.plot(vvix[vvix.index > '2024-12-31'].index, vvix[vvix.index > '2024-12-31']['High'], label='High', linestyle='-', color='steelblue', linewidth=1.5)
plt.plot(vvix[vvix.index > '2024-12-31'].index, vvix[vvix.index > '2024-12-31']['Low'], label='Low', linestyle='-', color='brown', linewidth=1.5)
# Set X axis
plt.gca().xaxis.set_major_locator(mdates.MonthLocator())
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
plt.xlabel("Date", fontsize=10)
plt.xticks(rotation=45, fontsize=8)
# Set Y axis
y_tick_spacing = 5 # Specify the interval for y-axis ticks
plt.gca().yaxis.set_major_locator(MultipleLocator(y_tick_spacing))
plt.ylabel("VVIX", fontsize=10)
plt.yticks(fontsize=8)
# Set title, layout, grid, and legend
plt.title("CBOE VVIX, 2025 - Present", fontsize=12)
plt.tight_layout()
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend(fontsize=9)
# Save figure and display plot
plt.savefig("02_VVIX_Plot_2025-Present.png", dpi=300, bbox_inches="tight")
plt.show()
Investigating A Signal¶
Determining A Spike Level¶
In [41]:
# Define the spike multiplier for detecting significant spikes
spike_level = 1.25
# =========================
# Simple Moving Averages (SMA)
# =========================
# Calculate 10-period SMA of 'High'
vix['High_SMA_10'] = vix['High'].rolling(window=10).mean()
# Shift the 10-period SMA by 1 to compare with current 'High'
vix['High_SMA_10_Shift'] = vix['High_SMA_10'].shift(1)
# Calculate the spike level based on shifted SMA and spike multiplier
vix['Spike_Level_SMA'] = vix['High_SMA_10_Shift'] * spike_level
# Calculate 20-period SMA of 'High'
vix['High_SMA_20'] = vix['High'].rolling(window=20).mean()
# Determine if 'High' exceeds the spike level (indicates a spike)
vix['Spike_SMA'] = vix['High'] >= vix['Spike_Level_SMA']
# Calculate 50-period SMA of 'High' for trend analysis
vix['High_SMA_50'] = vix['High'].rolling(window=50).mean()
# =========================
# Exponential Moving Averages (EMA)
# =========================
# Calculate 10-period EMA of 'High'
vix['High_EMA_10'] = vix['High'].ewm(span=10, adjust=False).mean()
# Shift the 10-period EMA by 1 to compare with current 'High'
vix['High_EMA_10_Shift'] = vix['High_EMA_10'].shift(1)
# Calculate the spike level based on shifted EMA and spike multiplier
vix['Spike_Level_EMA'] = vix['High_EMA_10_Shift'] * spike_level
# Calculate 20-period EMA of 'High'
vix['High_EMA_20'] = vix['High'].ewm(span=20, adjust=False).mean()
# Determine if 'High' exceeds the spike level (indicates a spike)
vix['Spike_EMA'] = vix['High'] >= vix['Spike_Level_EMA']
# Calculate 50-period EMA of 'High' for trend analysis
vix['High_EMA_50'] = vix['High'].ewm(span=50, adjust=False).mean()
In [42]:
display(vix)
Close | High | Low | Open | High_SMA_10 | High_SMA_10_Shift | Spike_Level_SMA | High_SMA_20 | Spike_SMA | High_SMA_50 | High_EMA_10 | High_EMA_10_Shift | Spike_Level_EMA | High_EMA_20 | Spike_EMA | High_EMA_50 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||||||||
1990-01-02 | 17.24 | 17.24 | 17.24 | 17.24 | NaN | NaN | NaN | NaN | False | NaN | 17.24 | NaN | NaN | 17.24 | False | 17.24 |
1990-01-03 | 18.19 | 18.19 | 18.19 | 18.19 | NaN | NaN | NaN | NaN | False | NaN | 17.41 | 17.24 | 21.55 | 17.33 | False | 17.28 |
1990-01-04 | 19.22 | 19.22 | 19.22 | 19.22 | NaN | NaN | NaN | NaN | False | NaN | 17.74 | 17.41 | 21.77 | 17.51 | False | 17.35 |
1990-01-05 | 20.11 | 20.11 | 20.11 | 20.11 | NaN | NaN | NaN | NaN | False | NaN | 18.17 | 17.74 | 22.18 | 17.76 | False | 17.46 |
1990-01-08 | 20.26 | 20.26 | 20.26 | 20.26 | NaN | NaN | NaN | NaN | False | NaN | 18.55 | 18.17 | 22.71 | 18.00 | False | 17.57 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2025-06-03 | 17.69 | 19.21 | 17.64 | 18.83 | 20.82 | 20.89 | 26.11 | 21.04 | False | 27.75 | 20.58 | 20.88 | 26.10 | 21.93 | False | 24.37 |
2025-06-04 | 17.61 | 18.07 | 17.41 | 17.68 | 20.76 | 20.82 | 26.02 | 20.68 | False | 27.72 | 20.12 | 20.58 | 25.72 | 21.57 | False | 24.13 |
2025-06-05 | 18.48 | 18.80 | 17.08 | 17.68 | 20.53 | 20.76 | 25.95 | 20.34 | False | 27.74 | 19.88 | 20.12 | 25.15 | 21.30 | False | 23.92 |
2025-06-06 | 16.77 | 18.35 | 16.65 | 18.16 | 20.16 | 20.53 | 25.66 | 20.08 | False | 27.73 | 19.60 | 19.88 | 24.85 | 21.02 | False | 23.70 |
2025-06-09 | 17.16 | 17.72 | 16.82 | 17.69 | 19.38 | 20.16 | 25.20 | 19.82 | False | 27.70 | 19.26 | 19.60 | 24.50 | 20.71 | False | 23.46 |
8925 rows × 16 columns
In [43]:
vix[vix['High'] >= 50]
Out[43]:
Close | High | Low | Open | High_SMA_10 | High_SMA_10_Shift | Spike_Level_SMA | High_SMA_20 | Spike_SMA | High_SMA_50 | High_EMA_10 | High_EMA_10_Shift | Spike_Level_EMA | High_EMA_20 | Spike_EMA | High_EMA_50 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||||||||
2008-10-06 | 52.05 | 58.24 | 45.12 | 45.12 | 42.92 | 40.52 | 50.65 | 37.24 | True | 28.17 | 44.33 | 41.24 | 51.55 | 38.82 | True | 31.65 |
2008-10-07 | 53.68 | 54.19 | 47.03 | 52.05 | 44.73 | 42.92 | 53.65 | 38.66 | True | 28.76 | 46.12 | 44.33 | 55.41 | 40.29 | False | 32.53 |
2008-10-08 | 57.53 | 59.06 | 51.90 | 53.68 | 46.97 | 44.73 | 55.91 | 40.34 | True | 29.46 | 48.47 | 46.12 | 57.65 | 42.07 | True | 33.57 |
2008-10-09 | 63.92 | 64.92 | 52.54 | 57.57 | 49.94 | 46.97 | 58.71 | 42.27 | True | 30.31 | 51.46 | 48.47 | 60.59 | 44.25 | True | 34.80 |
2008-10-10 | 69.95 | 76.94 | 65.63 | 65.85 | 53.99 | 49.94 | 62.42 | 44.79 | True | 31.39 | 56.10 | 51.46 | 64.33 | 47.36 | True | 36.46 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2024-08-05 | 38.57 | 65.73 | 23.39 | 23.39 | 23.84 | 18.95 | 23.69 | 19.11 | True | 15.66 | 28.04 | 19.66 | 24.58 | 22.15 | True | 17.62 |
2025-04-07 | 46.98 | 60.13 | 38.58 | 60.13 | 28.60 | 24.51 | 30.63 | 26.10 | True | 22.35 | 33.61 | 27.72 | 34.65 | 28.48 | True | 23.95 |
2025-04-08 | 52.33 | 57.52 | 36.48 | 44.04 | 32.58 | 28.60 | 35.76 | 27.50 | True | 23.05 | 37.96 | 33.61 | 42.01 | 31.25 | True | 25.27 |
2025-04-09 | 33.62 | 57.96 | 31.90 | 50.98 | 36.47 | 32.58 | 40.72 | 29.05 | True | 23.84 | 41.60 | 37.96 | 47.45 | 33.79 | True | 26.55 |
2025-04-10 | 40.72 | 54.87 | 34.44 | 34.44 | 40.03 | 36.47 | 45.59 | 30.49 | True | 24.58 | 44.01 | 41.60 | 51.99 | 35.80 | True | 27.66 |
97 rows × 16 columns
Spike Counts (Signals) By Year¶
In [44]:
# Ensure the index is a DatetimeIndex
vix.index = pd.to_datetime(vix.index)
# Create a new column for the year extracted from the date index
vix['Year'] = vix.index.year
# Group by year and the "Spike_SMA" and "Spike_EMA" columns, then count occurrences
spike_count_SMA = vix.groupby(['Year', 'Spike_SMA']).size().unstack(fill_value=0)
display(spike_count_SMA)
Spike_SMA | False | True |
---|---|---|
Year | ||
1990 | 248 | 5 |
1991 | 249 | 4 |
1992 | 250 | 4 |
1993 | 251 | 2 |
1994 | 243 | 9 |
1995 | 252 | 0 |
1996 | 248 | 6 |
1997 | 247 | 6 |
1998 | 243 | 9 |
1999 | 250 | 2 |
2000 | 248 | 4 |
2001 | 240 | 8 |
2002 | 248 | 4 |
2003 | 251 | 1 |
2004 | 250 | 2 |
2005 | 250 | 2 |
2006 | 242 | 9 |
2007 | 239 | 12 |
2008 | 238 | 15 |
2009 | 249 | 3 |
2010 | 239 | 13 |
2011 | 240 | 12 |
2012 | 248 | 2 |
2013 | 249 | 3 |
2014 | 235 | 17 |
2015 | 240 | 12 |
2016 | 234 | 18 |
2017 | 244 | 7 |
2018 | 228 | 23 |
2019 | 241 | 11 |
2020 | 224 | 29 |
2021 | 235 | 17 |
2022 | 239 | 12 |
2023 | 246 | 4 |
2024 | 237 | 15 |
2025 | 96 | 12 |
In [45]:
# Copy this <!-- INSERT_08_Spike_Counts_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="08_Spike_Counts.md", content=spike_count_SMA.to_markdown())
✅ Exported and tracked: 08_Spike_Counts.md
In [46]:
# Ensure the index is a DatetimeIndex
vix.index = pd.to_datetime(vix.index)
# Create a new column for the year extracted from the date index
vix['Year'] = vix.index.year
# Group by year and the "Spike_SMA" and "Spike_EMA" columns, then count occurrences
spike_count_EMA = vix.groupby(['Year', 'Spike_EMA']).size().unstack(fill_value=0)
display(spike_count_EMA)
Spike_EMA | False | True |
---|---|---|
Year | ||
1990 | 247 | 6 |
1991 | 251 | 2 |
1992 | 253 | 1 |
1993 | 251 | 2 |
1994 | 247 | 5 |
1995 | 252 | 0 |
1996 | 252 | 2 |
1997 | 250 | 3 |
1998 | 246 | 6 |
1999 | 250 | 2 |
2000 | 250 | 2 |
2001 | 241 | 7 |
2002 | 250 | 2 |
2003 | 251 | 1 |
2004 | 251 | 1 |
2005 | 250 | 2 |
2006 | 248 | 3 |
2007 | 242 | 9 |
2008 | 240 | 13 |
2009 | 251 | 1 |
2010 | 243 | 9 |
2011 | 242 | 10 |
2012 | 250 | 0 |
2013 | 250 | 2 |
2014 | 236 | 16 |
2015 | 243 | 9 |
2016 | 238 | 14 |
2017 | 244 | 7 |
2018 | 230 | 21 |
2019 | 242 | 10 |
2020 | 228 | 25 |
2021 | 239 | 13 |
2022 | 244 | 7 |
2023 | 248 | 2 |
2024 | 244 | 8 |
2025 | 99 | 9 |
In [47]:
# Plotting
plt.figure(figsize=(12, 6), facecolor="#F5F5F5")
# Bar positions
x = np.arange(len(spike_count_SMA[True].index))
width = 0.35
# Plot SMA bars
plt.bar(x - width / 2, spike_count_SMA[True].values, width, color="steelblue", label="Spike Counts Using SMA")
# Plot EMA bars
plt.bar(x + width / 2, spike_count_EMA[True].values, width, color="forestgreen", label="Spike Counts Using EMA")
# Set X axis
# x_tick_spacing = 5 # Specify the interval for y-axis ticks
# plt.gca().xaxis.set_major_locator(MultipleLocator(x_tick_spacing))
plt.xlabel("Year", fontsize=10)
plt.xticks(x, spike_count_SMA[True].index, rotation=45, fontsize=8)
plt.xlim(x[0] - 2 * width, x[-1] + 2 * width)
# # Set Y axis
y_tick_spacing = 2 # Specify the interval for y-axis ticks
plt.gca().yaxis.set_major_locator(MultipleLocator(y_tick_spacing))
plt.ylabel("Count", fontsize=10)
plt.yticks(fontsize=8)
# Set title, layout, grid, and legend
plt.title("Yearly Totals Of Spike Counts", fontsize=12)
plt.tight_layout()
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend(fontsize=9)
# Save figure and display plot
plt.savefig("08_Spike_Counts.png", dpi=300, bbox_inches="tight")
plt.show()
Spike Counts (Signals) Plots By Year¶
In [48]:
def vix_plot(start_year, end_year):
# Start and end dates
start_date = start_year + '-01-01'
end_date = end_year + '-12-31'
# Create temporary dataframe for the specified date range
vix_temp = vix[(vix.index >= start_date) & (vix.index <= end_date)]
# Plotting
plt.figure(figsize=(12, 6), facecolor="#F5F5F5")
# Plot data
plt.plot(vix_temp.index, vix_temp['High'], label='High', linestyle='-', color='steelblue', linewidth=1)
plt.plot(vix_temp.index, vix_temp['Low'], label='Low', linestyle='-', color='brown', linewidth=1)
plt.plot(vix_temp.index, vix_temp['High_SMA_10'], label='10 Day High SMA', linestyle='-', color='red', linewidth=1)
plt.plot(vix_temp.index, vix_temp['High_SMA_20'], label='20 Day High SMA', linestyle='-', color='orange', linewidth=1)
plt.plot(vix_temp.index, vix_temp['High_SMA_50'], label='50 Day High SMA', linestyle='-', color='green', linewidth=1)
plt.scatter(vix_temp[vix_temp['Spike_SMA'] == True].index, vix_temp[vix_temp['Spike_SMA'] == True]['High'], label='Spike (High > 1.25 * 10 Day High SMA)', linestyle='-', color='black', s=20)
# Set X axis
plt.gca().xaxis.set_major_locator(mdates.MonthLocator())
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
plt.xlabel("Date", fontsize=10)
plt.xticks(rotation=45, fontsize=8)
# Set Y axis
y_tick_spacing = 5 # Specify the interval for y-axis ticks
plt.gca().yaxis.set_major_locator(MultipleLocator(y_tick_spacing))
plt.ylabel("VIX", fontsize=10)
plt.yticks(fontsize=8)
# Set title, layout, grid, and legend
plt.title(f"CBOE Volatility Index (VIX), {start_year} - {end_year}", fontsize=12)
plt.tight_layout()
plt.grid(True, linestyle='--', alpha=0.7)
plt.legend(fontsize=9)
# Save figure and display plot
plt.savefig(f"09_VIX_SMA_Spike_{start_year}_{end_year}.png", dpi=300, bbox_inches="tight")
plt.show()
Yearly Plots¶
In [49]:
for year in range(1990, 2026):
vix_plot(str(year), str(year))
Spike Counts (Signals) Plots By Decade¶
1990 - 1994¶
In [50]:
vix_plot('1990', '1994')
1995 - 1999¶
In [51]:
vix_plot('1995', '1999')
2000 - 2004¶
In [52]:
vix_plot('2000', '2004')
2005 - 2009¶
In [53]:
vix_plot('2005', '2009')
2010 - 2014¶
In [54]:
vix_plot('2010', '2014')
2015 - 2019¶
In [55]:
vix_plot('2015', '2019')
2020 - 2024¶
In [56]:
vix_plot('2020', '2024')
2025 - Present¶
In [57]:
vix_plot('2025', '2029')
Trading History¶
Trades Executed¶
In [58]:
# from schwab_order_history import schwab_order_history
In [59]:
# from datetime import datetime
# import pandas as pd
# # Define your date ranges
# range_2024 = {
# "from": "2024-01-01T00:00:00.000Z",
# "to": "2024-12-31T23:59:59.000Z",
# }
# range_2025 = {
# "from": "2025-01-01T00:00:00.000Z",
# "to": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.000Z"),
# }
# # Pull both sets of orders
# df_2024 = schwab_order_history(
# max_results=1000, # or whatever large number you want
# from_entered_time=range_2024["from"],
# to_entered_time=range_2024["to"],
# account_id=None, # or pass your specific encrypted account ID
# )
# df_2025 = schwab_order_history(
# max_results=1000,
# from_entered_time=range_2025["from"],
# to_entered_time=range_2025["to"],
# account_id=None,
# )
# # Combine the two dataframes
# df_all = pd.concat([df_2024, df_2025], ignore_index=True)
In [60]:
# df_2024
In [61]:
# # Filter for symbols that start with "VIX"
# df_vix = df_all[df_all["symbol"].str.startswith("VIX")].copy()
# df_vix = df_vix.sort_values(by=['symbol', 'execution_time'], ascending=[True, True])
In [62]:
# df_vix
Trades Executed¶
In [63]:
# Import CSV file of VIX transactions from IRA and Brokerage accounts
vix_transactions_IRA = pd.read_csv(DATA_MANUAL_DIR / "VIX_Transactions_IRA.csv")
vix_transactions_Brokerage = pd.read_excel(DATA_MANUAL_DIR / "VIX_Transactions_Brokerage.xlsx", sheet_name="VIX_Transactions_Brokerage")
In [64]:
# Combine the two DataFrames
vix_transactions = pd.concat([vix_transactions_IRA, vix_transactions_Brokerage], ignore_index=True)
# Drop unnecessary columns
vix_transactions.drop(columns = {'Description'}, inplace=True)
# Convert Amount, Price, and Fees & Comm columns to numeric
vix_transactions['Amount'] = vix_transactions['Amount'].replace({'\$': '', ',': ''}, regex=True).astype(float)
vix_transactions['Price'] = vix_transactions['Price'].replace({'\$': '', ',': ''}, regex=True).astype(float)
vix_transactions['Fees & Comm'] = vix_transactions['Fees & Comm'].replace({'\$': '', ',': ''}, regex=True).astype(float)
# Convert Amount column to absolute values
vix_transactions['Amount'] = abs(vix_transactions['Amount'])
# Extract date for option expiration with regex (MM/DD/YYYY)
vix_transactions["Exp_Date"] = vix_transactions["Symbol"].str.extract(r'(\d{2}/\d{2}/\d{4})')
# Extract date for option strike price with regex and convert to float
vix_transactions["Strike_Price"] = vix_transactions["Symbol"].str.extract(r'(\d{2}\.\d{2})').astype(float)
# Convert expiration date and trade date to datetime
vix_transactions["Exp_Date"] = pd.to_datetime(vix_transactions["Exp_Date"], format="%m/%d/%Y")
vix_transactions['Date'] = pd.to_datetime(vix_transactions['Date'])
# Rename date to trade date
vix_transactions.rename(columns={'Date': 'Trade_Date'}, inplace=True)
# Sort by Exp_Date, then Strike_Price, then Trade_Date
vix_transactions.sort_values(by=['Exp_Date', 'Strike_Price', 'Trade_Date'], ascending=[True, True, True], inplace=True)
# Reset index
vix_transactions.reset_index(drop=True, inplace=True)
vix_transactions
Out[64]:
Trade_Date | Action | Symbol | Quantity | Price | Fees & Comm | Amount | Approx_VIX_Level | Comments | Exp_Date | Strike_Price | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2024-08-05 | Buy to Open | VIX 09/18/2024 34.00 P | 1 | 10.95 | 1.08 | 1096.08 | 34.33 | NaN | 2024-09-18 | 34.00 |
1 | 2024-08-21 | Sell to Close | VIX 09/18/2024 34.00 P | 1 | 17.95 | 1.08 | 1793.92 | 16.50 | NaN | 2024-09-18 | 34.00 |
2 | 2024-08-05 | Buy to Open | VIX 10/16/2024 40.00 P | 1 | 16.35 | 1.08 | 1636.08 | 42.71 | NaN | 2024-10-16 | 40.00 |
3 | 2024-09-18 | Sell to Close | VIX 10/16/2024 40.00 P | 1 | 21.54 | 1.08 | 2152.92 | 18.85 | NaN | 2024-10-16 | 40.00 |
4 | 2024-08-07 | Buy to Open | VIX 11/20/2024 25.00 P | 2 | 5.90 | 2.16 | 1182.16 | 27.11 | NaN | 2024-11-20 | 25.00 |
5 | 2024-11-04 | Sell to Close | VIX 11/20/2024 25.00 P | 2 | 6.10 | 2.16 | 1217.84 | 22.43 | NaN | 2024-11-20 | 25.00 |
6 | 2024-08-06 | Buy to Open | VIX 12/18/2024 30.00 P | 1 | 10.25 | 1.08 | 1026.08 | 32.27 | NaN | 2024-12-18 | 30.00 |
7 | 2024-11-27 | Sell to Close | VIX 12/18/2024 30.00 P | 1 | 14.95 | 1.08 | 1493.92 | 14.04 | NaN | 2024-12-18 | 30.00 |
8 | 2025-03-04 | Buy to Open | VIX 04/16/2025 25.00 P | 5 | 5.65 | 5.40 | 2830.40 | 25.75 | NaN | 2025-04-16 | 25.00 |
9 | 2025-03-24 | Sell to Close | VIX 04/16/2025 25.00 P | 5 | 7.00 | 5.40 | 3494.60 | 18.01 | NaN | 2025-04-16 | 25.00 |
10 | 2025-03-10 | Buy to Open | VIX 05/21/2025 26.00 P | 5 | 7.10 | 5.40 | 3555.40 | 27.54 | Missed opportunity to close position for 20% p... | 2025-05-21 | 26.00 |
11 | 2025-04-04 | Buy to Open | VIX 05/21/2025 26.00 P | 10 | 4.10 | 10.81 | 4110.81 | 38.88 | Averaged down on existing position | 2025-05-21 | 26.00 |
12 | 2025-04-24 | Sell to Close | VIX 05/21/2025 26.00 P | 7 | 3.50 | 7.57 | 2442.43 | 27.37 | Sold half of position due to vol spike concern... | 2025-05-21 | 26.00 |
13 | 2025-05-02 | Sell to Close | VIX 05/21/2025 26.00 P | 4 | 4.35 | 4.32 | 1735.68 | 22.73 | Sold half of remaining position due to vol spi... | 2025-05-21 | 26.00 |
14 | 2025-05-07 | Sell to Close | VIX 05/21/2025 26.00 P | 4 | 3.55 | 4.32 | 1415.68 | 24.49 | Closed position ahead of Fed’s (Powell’s) comm... | 2025-05-21 | 26.00 |
15 | 2025-04-04 | Buy to Open | VIX 05/21/2025 37.00 P | 3 | 13.20 | 3.24 | 3963.24 | 36.46 | NaN | 2025-05-21 | 37.00 |
16 | 2025-05-07 | Sell to Close | VIX 05/21/2025 37.00 P | 3 | 13.75 | 3.24 | 4121.76 | 24.51 | Closed position ahead of Fed’s (Powell’s) comm... | 2025-05-21 | 37.00 |
17 | 2025-04-08 | Buy to Open | VIX 05/21/2025 50.00 P | 2 | 21.15 | 2.16 | 4232.16 | NaN | NaN | 2025-05-21 | 50.00 |
18 | 2025-04-24 | Sell to Close | VIX 05/21/2025 50.00 P | 1 | 25.30 | 1.08 | 2528.92 | NaN | NaN | 2025-05-21 | 50.00 |
19 | 2025-04-25 | Sell to Close | VIX 05/21/2025 50.00 P | 1 | 25.65 | 1.08 | 2563.92 | NaN | NaN | 2025-05-21 | 50.00 |
20 | 2025-04-03 | Buy to Open | VIX 06/18/2025 27.00 P | 8 | 7.05 | 8.65 | 5648.65 | 27.62 | NaN | 2025-06-18 | 27.00 |
21 | 2025-04-08 | Buy to Open | VIX 06/18/2025 27.00 P | 4 | 4.55 | 4.32 | 1824.32 | 55.44 | Averaged down on existing position | 2025-06-18 | 27.00 |
22 | 2025-05-12 | Sell to Close | VIX 06/18/2025 27.00 P | 6 | 7.55 | 6.49 | 4523.51 | 19.05 | Market up on positive news of lowering tariffs... | 2025-06-18 | 27.00 |
23 | 2025-05-12 | Sell to Close | VIX 06/18/2025 27.00 P | 6 | 7.40 | 6.49 | 4433.51 | 19.47 | Market up on positive news of lowering tariffs... | 2025-06-18 | 27.00 |
24 | 2025-04-04 | Buy to Open | VIX 06/18/2025 36.00 P | 3 | 13.40 | 3.24 | 4023.24 | 36.61 | NaN | 2025-06-18 | 36.00 |
25 | 2025-05-12 | Sell to Close | VIX 06/18/2025 36.00 P | 3 | 16.00 | 3.24 | 4796.76 | 19.14 | Market up on positive news of lowering tariffs... | 2025-06-18 | 36.00 |
26 | 2025-04-07 | Buy to Open | VIX 06/18/2025 45.00 P | 2 | 18.85 | 2.16 | 3772.16 | 53.65 | NaN | 2025-06-18 | 45.00 |
27 | 2025-05-12 | Sell to Close | VIX 06/18/2025 45.00 P | 2 | 25.00 | 2.16 | 4997.84 | 19.24 | Market up on positive news of lowering tariffs... | 2025-06-18 | 45.00 |
28 | 2025-04-03 | Buy to Open | VIX 07/16/2025 29.00 P | 5 | 8.55 | 5.40 | 4280.40 | 29.03 | NaN | 2025-07-16 | 29.00 |
29 | 2025-05-13 | Sell to Close | VIX 07/16/2025 29.00 P | 3 | 10.40 | 3.24 | 3116.76 | 17.72 | NaN | 2025-07-16 | 29.00 |
30 | 2025-05-13 | Sell to Close | VIX 07/16/2025 29.00 P | 2 | 10.30 | 2.16 | 2057.84 | 17.68 | NaN | 2025-07-16 | 29.00 |
31 | 2025-04-04 | Buy to Open | VIX 07/16/2025 36.00 P | 3 | 13.80 | 3.24 | 4143.24 | 36.95 | NaN | 2025-07-16 | 36.00 |
32 | 2025-05-13 | Sell to Close | VIX 07/16/2025 36.00 P | 1 | 17.00 | 1.08 | 1698.92 | 17.79 | NaN | 2025-07-16 | 36.00 |
33 | 2025-05-13 | Sell to Close | VIX 07/16/2025 36.00 P | 2 | 16.90 | 2.16 | 3377.84 | 17.72 | NaN | 2025-07-16 | 36.00 |
34 | 2025-04-07 | Buy to Open | VIX 07/16/2025 45.00 P | 2 | 21.55 | 2.16 | 4312.16 | 46.17 | NaN | 2025-07-16 | 45.00 |
35 | 2025-05-13 | Sell to Close | VIX 07/16/2025 45.00 P | 2 | 25.65 | 2.16 | 5127.84 | 17.96 | NaN | 2025-07-16 | 45.00 |
36 | 2025-04-07 | Buy to Open | VIX 08/20/2025 45.00 P | 2 | 21.75 | 2.16 | 4352.16 | 49.07 | NaN | 2025-08-20 | 45.00 |
37 | 2025-05-13 | Sell to Close | VIX 08/20/2025 45.00 P | 2 | 25.40 | 2.16 | 5077.84 | 18.06 | NaN | 2025-08-20 | 45.00 |
In [65]:
vix_transactions_no_exp = vix_transactions.drop(columns=['Exp_Date', 'Strike_Price'])
vix_transactions_no_exp
Out[65]:
Trade_Date | Action | Symbol | Quantity | Price | Fees & Comm | Amount | Approx_VIX_Level | Comments | |
---|---|---|---|---|---|---|---|---|---|
0 | 2024-08-05 | Buy to Open | VIX 09/18/2024 34.00 P | 1 | 10.95 | 1.08 | 1096.08 | 34.33 | NaN |
1 | 2024-08-21 | Sell to Close | VIX 09/18/2024 34.00 P | 1 | 17.95 | 1.08 | 1793.92 | 16.50 | NaN |
2 | 2024-08-05 | Buy to Open | VIX 10/16/2024 40.00 P | 1 | 16.35 | 1.08 | 1636.08 | 42.71 | NaN |
3 | 2024-09-18 | Sell to Close | VIX 10/16/2024 40.00 P | 1 | 21.54 | 1.08 | 2152.92 | 18.85 | NaN |
4 | 2024-08-07 | Buy to Open | VIX 11/20/2024 25.00 P | 2 | 5.90 | 2.16 | 1182.16 | 27.11 | NaN |
5 | 2024-11-04 | Sell to Close | VIX 11/20/2024 25.00 P | 2 | 6.10 | 2.16 | 1217.84 | 22.43 | NaN |
6 | 2024-08-06 | Buy to Open | VIX 12/18/2024 30.00 P | 1 | 10.25 | 1.08 | 1026.08 | 32.27 | NaN |
7 | 2024-11-27 | Sell to Close | VIX 12/18/2024 30.00 P | 1 | 14.95 | 1.08 | 1493.92 | 14.04 | NaN |
8 | 2025-03-04 | Buy to Open | VIX 04/16/2025 25.00 P | 5 | 5.65 | 5.40 | 2830.40 | 25.75 | NaN |
9 | 2025-03-24 | Sell to Close | VIX 04/16/2025 25.00 P | 5 | 7.00 | 5.40 | 3494.60 | 18.01 | NaN |
10 | 2025-03-10 | Buy to Open | VIX 05/21/2025 26.00 P | 5 | 7.10 | 5.40 | 3555.40 | 27.54 | Missed opportunity to close position for 20% p... |
11 | 2025-04-04 | Buy to Open | VIX 05/21/2025 26.00 P | 10 | 4.10 | 10.81 | 4110.81 | 38.88 | Averaged down on existing position |
12 | 2025-04-24 | Sell to Close | VIX 05/21/2025 26.00 P | 7 | 3.50 | 7.57 | 2442.43 | 27.37 | Sold half of position due to vol spike concern... |
13 | 2025-05-02 | Sell to Close | VIX 05/21/2025 26.00 P | 4 | 4.35 | 4.32 | 1735.68 | 22.73 | Sold half of remaining position due to vol spi... |
14 | 2025-05-07 | Sell to Close | VIX 05/21/2025 26.00 P | 4 | 3.55 | 4.32 | 1415.68 | 24.49 | Closed position ahead of Fed’s (Powell’s) comm... |
15 | 2025-04-04 | Buy to Open | VIX 05/21/2025 37.00 P | 3 | 13.20 | 3.24 | 3963.24 | 36.46 | NaN |
16 | 2025-05-07 | Sell to Close | VIX 05/21/2025 37.00 P | 3 | 13.75 | 3.24 | 4121.76 | 24.51 | Closed position ahead of Fed’s (Powell’s) comm... |
17 | 2025-04-08 | Buy to Open | VIX 05/21/2025 50.00 P | 2 | 21.15 | 2.16 | 4232.16 | NaN | NaN |
18 | 2025-04-24 | Sell to Close | VIX 05/21/2025 50.00 P | 1 | 25.30 | 1.08 | 2528.92 | NaN | NaN |
19 | 2025-04-25 | Sell to Close | VIX 05/21/2025 50.00 P | 1 | 25.65 | 1.08 | 2563.92 | NaN | NaN |
20 | 2025-04-03 | Buy to Open | VIX 06/18/2025 27.00 P | 8 | 7.05 | 8.65 | 5648.65 | 27.62 | NaN |
21 | 2025-04-08 | Buy to Open | VIX 06/18/2025 27.00 P | 4 | 4.55 | 4.32 | 1824.32 | 55.44 | Averaged down on existing position |
22 | 2025-05-12 | Sell to Close | VIX 06/18/2025 27.00 P | 6 | 7.55 | 6.49 | 4523.51 | 19.05 | Market up on positive news of lowering tariffs... |
23 | 2025-05-12 | Sell to Close | VIX 06/18/2025 27.00 P | 6 | 7.40 | 6.49 | 4433.51 | 19.47 | Market up on positive news of lowering tariffs... |
24 | 2025-04-04 | Buy to Open | VIX 06/18/2025 36.00 P | 3 | 13.40 | 3.24 | 4023.24 | 36.61 | NaN |
25 | 2025-05-12 | Sell to Close | VIX 06/18/2025 36.00 P | 3 | 16.00 | 3.24 | 4796.76 | 19.14 | Market up on positive news of lowering tariffs... |
26 | 2025-04-07 | Buy to Open | VIX 06/18/2025 45.00 P | 2 | 18.85 | 2.16 | 3772.16 | 53.65 | NaN |
27 | 2025-05-12 | Sell to Close | VIX 06/18/2025 45.00 P | 2 | 25.00 | 2.16 | 4997.84 | 19.24 | Market up on positive news of lowering tariffs... |
28 | 2025-04-03 | Buy to Open | VIX 07/16/2025 29.00 P | 5 | 8.55 | 5.40 | 4280.40 | 29.03 | NaN |
29 | 2025-05-13 | Sell to Close | VIX 07/16/2025 29.00 P | 3 | 10.40 | 3.24 | 3116.76 | 17.72 | NaN |
30 | 2025-05-13 | Sell to Close | VIX 07/16/2025 29.00 P | 2 | 10.30 | 2.16 | 2057.84 | 17.68 | NaN |
31 | 2025-04-04 | Buy to Open | VIX 07/16/2025 36.00 P | 3 | 13.80 | 3.24 | 4143.24 | 36.95 | NaN |
32 | 2025-05-13 | Sell to Close | VIX 07/16/2025 36.00 P | 1 | 17.00 | 1.08 | 1698.92 | 17.79 | NaN |
33 | 2025-05-13 | Sell to Close | VIX 07/16/2025 36.00 P | 2 | 16.90 | 2.16 | 3377.84 | 17.72 | NaN |
34 | 2025-04-07 | Buy to Open | VIX 07/16/2025 45.00 P | 2 | 21.55 | 2.16 | 4312.16 | 46.17 | NaN |
35 | 2025-05-13 | Sell to Close | VIX 07/16/2025 45.00 P | 2 | 25.65 | 2.16 | 5127.84 | 17.96 | NaN |
36 | 2025-04-07 | Buy to Open | VIX 08/20/2025 45.00 P | 2 | 21.75 | 2.16 | 4352.16 | 49.07 | NaN |
37 | 2025-05-13 | Sell to Close | VIX 08/20/2025 45.00 P | 2 | 25.40 | 2.16 | 5077.84 | 18.06 | NaN |
In [66]:
# Copy this <!-- INSERT_10_Trades_Executed_HERE --> to index_temp.md
export_track_md_deps(dep_file=dep_file, md_filename="10_Trades_Executed.md", content=vix_transactions_no_exp.to_markdown(index=False, floatfmt=".2f"))
✅ Exported and tracked: 10_Trades_Executed.md
Volatility In August 2024¶
In [67]:
esd = "2024-09-18"
eed = "2024-12-18"
tsd = "2024-08-05"
ted = "2024-11-27"
trades, closed_pos, open_pos, per_pnl, pnl = calc_vix_trade_pnl(
transaction_df=vix_transactions,
exp_start_date=esd,
exp_end_date=eed,
trade_start_date=tsd,
trade_end_date=ted,
)
# Convert to datetime objects
tsd_dt = datetime.strptime(tsd, "%Y-%m-%d")
ted_dt = datetime.strptime(ted, "%Y-%m-%d")
# Adjust dates
plot_start = tsd_dt - timedelta(days=10)
plot_end = ted_dt + timedelta(days=10)
plot_vix_with_trades(
vix_price_df=vix,
trades_df=trades,
plot_start_date=plot_start.strftime("%Y-%m-%d"),
plot_end_date=plot_end.strftime("%Y-%m-%d"),
x_tick_spacing=10,
y_tick_spacing=5,
index_number="11",
export_plot=True,
)
print(f"<!-- INSERT_11_Closed_Positions_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_11_Open_Positions_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_11_Percent_PnL_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_11_PnL_{esd}_{eed}_{tsd}_{ted}_HERE -->")
export_track_md_deps(dep_file=dep_file, md_filename=f"11_Closed_Positions_{esd}_{eed}_{tsd}_{ted}.md", content=closed_pos.to_markdown(index=False, floatfmt=".2f"))
export_track_md_deps(dep_file=dep_file, md_filename=f"11_Open_Positions_{esd}_{eed}_{tsd}_{ted}.md", content=open_pos.to_markdown(index=False, floatfmt=".2f"))
export_track_md_deps(dep_file=dep_file, md_filename=f"11_Percent_PnL_{esd}_{eed}_{tsd}_{ted}.md", content=per_pnl)
export_track_md_deps(dep_file=dep_file, md_filename=f"11_PnL_{esd}_{eed}_{tsd}_{ted}.md", content=pnl)
<!-- INSERT_11_Closed_Positions_2024-09-18_2024-12-18_2024-08-05_2024-11-27_HERE --> <!-- INSERT_11_Open_Positions_2024-09-18_2024-12-18_2024-08-05_2024-11-27_HERE --> <!-- INSERT_11_Percent_PnL_2024-09-18_2024-12-18_2024-08-05_2024-11-27_HERE --> <!-- INSERT_11_PnL_2024-09-18_2024-12-18_2024-08-05_2024-11-27_HERE --> ✅ Exported and tracked: 11_Closed_Positions_2024-09-18_2024-12-18_2024-08-05_2024-11-27.md ✅ Exported and tracked: 11_Open_Positions_2024-09-18_2024-12-18_2024-08-05_2024-11-27.md ✅ Exported and tracked: 11_Percent_PnL_2024-09-18_2024-12-18_2024-08-05_2024-11-27.md ✅ Exported and tracked: 11_PnL_2024-09-18_2024-12-18_2024-08-05_2024-11-27.md
Volatility In March 2025¶
In [68]:
esd = "2025-04-16"
eed = "2025-04-16"
tsd = "2025-03-04"
ted = "2025-03-24"
trades, closed_pos, open_pos, per_pnl, pnl = calc_vix_trade_pnl(
transaction_df=vix_transactions,
exp_start_date=esd,
exp_end_date=eed,
trade_start_date=tsd,
trade_end_date=ted,
)
# Convert to datetime objects
tsd_dt = datetime.strptime(tsd, "%Y-%m-%d")
ted_dt = datetime.strptime(ted, "%Y-%m-%d")
# Adjust dates
plot_start = tsd_dt - timedelta(days=10)
plot_end = ted_dt + timedelta(days=10)
plot_vix_with_trades(
vix_price_df=vix,
trades_df=trades,
plot_start_date=plot_start.strftime("%Y-%m-%d"),
plot_end_date=plot_end.strftime("%Y-%m-%d"),
x_tick_spacing=2,
y_tick_spacing=2,
index_number="12",
export_plot=True,
)
print(f"<!-- INSERT_12_Closed_Positions_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_12_Open_Positions_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_12_Percent_PnL_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_12_PnL_{esd}_{eed}_{tsd}_{ted}_HERE -->")
export_track_md_deps(dep_file=dep_file, md_filename=f"12_Closed_Positions_{esd}_{eed}_{tsd}_{ted}.md", content=closed_pos.to_markdown(index=False, floatfmt=".2f"))
export_track_md_deps(dep_file=dep_file, md_filename=f"12_Open_Positions_{esd}_{eed}_{tsd}_{ted}.md", content=open_pos.to_markdown(index=False, floatfmt=".2f"))
export_track_md_deps(dep_file=dep_file, md_filename=f"12_Percent_PnL_{esd}_{eed}_{tsd}_{ted}.md", content=per_pnl)
export_track_md_deps(dep_file=dep_file, md_filename=f"12_PnL_{esd}_{eed}_{tsd}_{ted}.md", content=pnl)
<!-- INSERT_12_Closed_Positions_2025-04-16_2025-04-16_2025-03-04_2025-03-24_HERE --> <!-- INSERT_12_Open_Positions_2025-04-16_2025-04-16_2025-03-04_2025-03-24_HERE --> <!-- INSERT_12_Percent_PnL_2025-04-16_2025-04-16_2025-03-04_2025-03-24_HERE --> <!-- INSERT_12_PnL_2025-04-16_2025-04-16_2025-03-04_2025-03-24_HERE --> ✅ Exported and tracked: 12_Closed_Positions_2025-04-16_2025-04-16_2025-03-04_2025-03-24.md ✅ Exported and tracked: 12_Open_Positions_2025-04-16_2025-04-16_2025-03-04_2025-03-24.md ✅ Exported and tracked: 12_Percent_PnL_2025-04-16_2025-04-16_2025-03-04_2025-03-24.md ✅ Exported and tracked: 12_PnL_2025-04-16_2025-04-16_2025-03-04_2025-03-24.md
Volatility In April 2025¶
In [69]:
esd = "2025-05-21"
eed = "2025-08-20"
tsd = "2025-03-10"
ted = "2025-05-13"
trades, closed_pos, open_pos, per_pnl, pnl = calc_vix_trade_pnl(
transaction_df=vix_transactions,
exp_start_date=esd,
exp_end_date=eed,
trade_start_date=tsd,
trade_end_date=ted,
)
# Convert to datetime objects
tsd_dt = datetime.strptime(tsd, "%Y-%m-%d")
ted_dt = datetime.strptime(ted, "%Y-%m-%d")
# Adjust dates
plot_start = tsd_dt - timedelta(days=10)
plot_end = ted_dt + timedelta(days=10)
plot_vix_with_trades(
vix_price_df=vix,
trades_df=trades,
plot_start_date=plot_start.strftime("%Y-%m-%d"),
plot_end_date=plot_end.strftime("%Y-%m-%d"),
x_tick_spacing=5,
y_tick_spacing=5,
index_number="13",
export_plot=True,
)
print(f"<!-- INSERT_13_Closed_Positions_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_13_Open_Positions_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_13_Percent_PnL_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_13_PnL_{esd}_{eed}_{tsd}_{ted}_HERE -->")
export_track_md_deps(dep_file=dep_file, md_filename=f"13_Closed_Positions_{esd}_{eed}_{tsd}_{ted}.md", content=closed_pos.to_markdown(index=False, floatfmt=".2f"))
export_track_md_deps(dep_file=dep_file, md_filename=f"13_Open_Positions_{esd}_{eed}_{tsd}_{ted}.md", content=open_pos.to_markdown(index=False, floatfmt=".2f"))
export_track_md_deps(dep_file=dep_file, md_filename=f"13_Percent_PnL_{esd}_{eed}_{tsd}_{ted}.md", content=per_pnl)
export_track_md_deps(dep_file=dep_file, md_filename=f"13_PnL_{esd}_{eed}_{tsd}_{ted}.md", content=pnl)
<!-- INSERT_13_Closed_Positions_2025-05-21_2025-08-20_2025-03-10_2025-05-13_HERE --> <!-- INSERT_13_Open_Positions_2025-05-21_2025-08-20_2025-03-10_2025-05-13_HERE --> <!-- INSERT_13_Percent_PnL_2025-05-21_2025-08-20_2025-03-10_2025-05-13_HERE --> <!-- INSERT_13_PnL_2025-05-21_2025-08-20_2025-03-10_2025-05-13_HERE --> ✅ Exported and tracked: 13_Closed_Positions_2025-05-21_2025-08-20_2025-03-10_2025-05-13.md ✅ Exported and tracked: 13_Open_Positions_2025-05-21_2025-08-20_2025-03-10_2025-05-13.md ✅ Exported and tracked: 13_Percent_PnL_2025-05-21_2025-08-20_2025-03-10_2025-05-13.md ✅ Exported and tracked: 13_PnL_2025-05-21_2025-08-20_2025-03-10_2025-05-13.md
Complete Trade History¶
In [70]:
esd = None
eed = None
tsd = None
ted = None
trades, closed_pos, open_pos, per_pnl, pnl = calc_vix_trade_pnl(
transaction_df=vix_transactions,
exp_start_date=esd,
exp_end_date=eed,
trade_start_date=tsd,
trade_end_date=ted,
)
print(f"<!-- INSERT_14_Closed_Positions_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_14_Open_Positions_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_14_Percent_PnL_{esd}_{eed}_{tsd}_{ted}_HERE -->")
print(f"<!-- INSERT_14_PnL_{esd}_{eed}_{tsd}_{ted}_HERE -->")
export_track_md_deps(dep_file=dep_file, md_filename=f"14_Closed_Positions_{esd}_{eed}_{tsd}_{ted}.md", content=closed_pos.to_markdown(index=False, floatfmt=".2f"))
export_track_md_deps(dep_file=dep_file, md_filename=f"14_Open_Positions_{esd}_{eed}_{tsd}_{ted}.md", content=open_pos.to_markdown(index=False, floatfmt=".2f"))
export_track_md_deps(dep_file=dep_file, md_filename=f"14_Percent_PnL_{esd}_{eed}_{tsd}_{ted}.md", content=per_pnl)
export_track_md_deps(dep_file=dep_file, md_filename=f"14_PnL_{esd}_{eed}_{tsd}_{ted}.md", content=pnl)
<!-- INSERT_14_Closed_Positions_None_None_None_None_HERE --> <!-- INSERT_14_Open_Positions_None_None_None_None_HERE --> <!-- INSERT_14_Percent_PnL_None_None_None_None_HERE --> <!-- INSERT_14_PnL_None_None_None_None_HERE --> ✅ Exported and tracked: 14_Closed_Positions_None_None_None_None.md ✅ Exported and tracked: 14_Open_Positions_None_None_None_None.md ✅ Exported and tracked: 14_Percent_PnL_None_None_None_None.md ✅ Exported and tracked: 14_PnL_None_None_None_None.md