Mathew K Analytics

Lesson 12 · Finance and Stock Market Analytics

Understanding OHLCV Data in Finance: Open, High, Low, Close, and Volume Explained

In this lesson, we will explore the structure, meaning, and use of OHLCV data in finance and stock market analytics. OHLCV data describes daily price and…

📓 Full notebook

Download .ipynb

Understanding OHLCV Data (Open, High, Low, Close, Volume)#

  • In this lesson, we will explore the structure, meaning, and use of OHLCV data in finance and stock market analytics.
  • OHLCV data describes daily price and trading activity for stocks and is fundamental to financial analysis.
  • You will learn to load, inspect, filter, reshape, and analyze real OHLCV data for multiple companies using Python.
  • By the end, you will understand how to apply OHLCV to solve typical finance problems like return calculation, simple signals, and basic error handling.
import pandas as pd
import yfinance as yf
import numpy as np
import warnings
warnings.filterwarnings('ignore')

What is OHLCV Data?#

  • OHLCV stands for Open, High, Low, Close, and Volume.
  • Each value shows a different aspect of stock price and trading for a single day.
  • Open: Price at the start of the trading day.
  • High: Highest price traded during the day.
  • Low: Lowest price traded during the day.
  • Close: Final price at the end of the trading day.
  • Volume: Number of shares traded during the day.
  • OHLCV data is usually recorded for each stock and each day on the market.

How is OHLCV Data Structured?#

  • In Python, OHLCV is commonly managed in DataFrames, where each row is a day and ticker.
  • Columns include Date, Ticker, Open, High, Low, Close, and Volume.
  • Multi-ticker data can be stored in long (tidy) or wide (pivot) formats.
  • Beginners often confuse column orders, ticker positions, or try to flatten indices incorrectly.

Why Does OHLCV Matter in Stock Analytics?#

  • OHLCV enables return, volatility, drawdown, and pattern calculations.
  • It powers almost all charting, trading signals, and portfolio analytics.
  • Clean OHLCV understanding is required to interpret any price-based insight in finance.
# Load multi-ticker OHLCV data for 5 major US tech companies (1 year)
tickers = ['AAPL','MSFT','GOOGL','AMZN','TSLA']
ohlcv = yf.download(tickers, period='1y', auto_adjust=True, progress=False)
ohlcv = ohlcv.stack(future_stack=True).rename_axis(['Date','Ticker']).reset_index()
ohlcv.columns.name = None
print(ohlcv.shape)
print(ohlcv.head(3))
(1255, 7)
        Date Ticker       Close        High         Low        Open    Volume
0 2025-07-14   AAPL  207.795624  210.076583  206.719890  209.100445  38840100
1 2025-07-14   AMZN  225.690002  226.660004  224.240005  225.070007  35702600
2 2025-07-14  GOOGL  181.043518  183.147516  179.168861  180.495080  32536600

Beginner Example 1: Filtering by Ticker#

  • To get OHLCV for a single stock on all trading days, filter on the Ticker column.
  • This lets you analyze just one company's price and volume movements.
# Select only Apple (AAPL) data from the long OHLCV DataFrame
aapl_ohlcv = ohlcv[ohlcv['Ticker'] == 'AAPL']
print(aapl_ohlcv.head(5))
         Date Ticker       Close        High         Low        Open    Volume
0  2025-07-14   AAPL  207.795624  210.076583  206.719890  209.100445  38840100
5  2025-07-15   AAPL  208.283707  211.052720  208.094455  208.393273  42296300
10 2025-07-16   AAPL  209.329529  211.560667  207.815531  209.468975  47490500
15 2025-07-17   AAPL  209.190109  210.963074  208.761800  209.737939  48068100
20 2025-07-18   AAPL  210.345505  210.953095  208.871357  210.036732  48974600

Beginner Example 2: Filtering by Date#

  • You can select rows for a specific date or a range of dates.
  • This is useful for finding important trading days or events.
# Select TSLA data for the first trading week
first_week = ohlcv[(ohlcv['Ticker'] == 'TSLA') & (ohlcv['Date'] <= ohlcv[ohlcv['Ticker']=='TSLA']['Date'].iloc[4])]
print(first_week)
         Date Ticker       Close        High         Low        Open    Volume
4  2025-07-14   TSLA  316.899994  322.600006  312.670013  317.730011  78043400
9  2025-07-15   TSLA  310.779999  321.200012  310.500000  319.679993  77556300
14 2025-07-16   TSLA  321.670013  323.500000  312.619995  312.799988  97284800
19 2025-07-17   TSLA  319.410004  324.339996  317.059998  323.149994  73922900
24 2025-07-18   TSLA  329.649994  330.899994  321.420013  321.660004  94255000

Beginner Example 3: Sorting by Price or Volume#

  • To find most volatile or highest volume days, sort rows by High, Low, or Volume.
  • Sorting helps discover outlier days and trading spikes.
# Find top 3 highest volume days for Google (GOOGL)
googl = ohlcv[ohlcv['Ticker'] == 'GOOGL']
top_vol = googl.sort_values('Volume', ascending=False).head(3)
print(top_vol[['Date','Volume','Close']])
           Date     Volume       Close
1202 2026-06-26  114706300  337.390015
182  2025-09-03  103336100  230.003860
477  2025-11-25   88632100  322.808380

Intermediate Example 1: Pivoting OHLCV to Wide Format#

  • Sometimes, analysts want one row per date with a separate column per ticker.
  • Pivoting reorganizes tidy OHLCV into a 'wide' price table for comparisons or charts.
# Pivot to create a wide Close price table (dates as rows, tickers as columns)
close_wide = ohlcv.pivot(index='Date', columns='Ticker', values='Close')
print(close_wide.head())
Ticker            AAPL        AMZN       GOOGL        MSFT        TSLA
Date                                                                  
2025-07-14  207.795624  225.690002  181.043518  499.033936  316.899994
2025-07-15  208.283707  226.350006  181.482269  501.811737  310.779999
2025-07-16  209.329529  223.190002  182.449524  501.613312  321.670013
2025-07-17  209.190109  223.880005  183.057785  507.645142  319.410004
2025-07-18  210.345505  226.130005  184.533554  506.008209  329.649994

Intermediate Example 2: Calculating Daily Returns#

  • Financial analysis often starts with returns, not just prices.
  • You can calculate percent changes in Close prices to find daily returns per stock.
# Calculate daily returns for each ticker
ohlcv['Return'] = ohlcv.groupby('Ticker')['Close'].pct_change() * 100
print(ohlcv[['Date','Ticker','Close','Return']].head(8))
        Date Ticker       Close    Return
0 2025-07-14   AAPL  207.795624       NaN
1 2025-07-14   AMZN  225.690002       NaN
2 2025-07-14  GOOGL  181.043518       NaN
3 2025-07-14   MSFT  499.033936       NaN
4 2025-07-14   TSLA  316.899994       NaN
5 2025-07-15   AAPL  208.283707  0.234886
6 2025-07-15   AMZN  226.350006  0.292438
7 2025-07-15  GOOGL  181.482269  0.242346

Intermediate Example 3: Filtering Volatile Trading Days#

  • Large differences between High and Low signal volatility.
  • Find all days where the intraday range exceeds a threshold (e.g. 6%).
# Find all AMZN days where High-Low > 6% of Open price
amzn = ohlcv[ohlcv['Ticker']=='AMZN'].copy()
amzn['RangePct'] = (amzn['High'] - amzn['Low']) / amzn['Open'] * 100
volatile = amzn[amzn['RangePct'] > 6]
print(volatile[['Date','Open','High','Low','RangePct']].head(4))
           Date        Open        High         Low  RangePct
1006 2026-04-30  273.170013  273.880005  256.160004  6.486803
1206 2026-06-29  234.220001  249.710007  233.800003  6.792760

Advanced Example 1: Querying With Multiple Criteria#

  • Analysts often need to combine filters, such as high Volume and large price drops.
  • This allows targeting rare or impactful market events.
# Top 5 TSLA days with both huge Volume and negative returns
tsla = ohlcv[ohlcv['Ticker'] == 'TSLA']
threshold = tsla['Volume'].quantile(0.95)
big_down = tsla[(tsla['Volume'] > threshold) & (tsla['Return'] < -4)]
print(big_down[['Date','Volume','Return','Close']].head())
          Date     Volume    Return       Close
44  2025-07-24  156966000 -8.197020  305.299988
289 2025-10-02  137009000 -5.105992  436.000000
319 2025-10-10  112107900 -5.062685  413.489990
439 2025-11-13  118948000 -6.644221  401.989990

Advanced Example 2: Visualizing OHLCV Data#

  • Visualization reveals trends, cycles, and anomalies.
  • You can use matplotlib to plot OHLC, closing price, or trading volume series.
import matplotlib.pyplot as plt
# Plot AAPL closing price and volume on two axes
aapl = ohlcv[ohlcv['Ticker']=='AAPL']
fig, ax1 = plt.subplots(figsize=(9,4))
ax1.plot(aapl['Date'], aapl['Close'], color='b', label='Close Price')
ax1.set_ylabel('Price (USD)', color='b')
ax2 = ax1.twinx()
ax2.bar(aapl['Date'], aapl['Volume'], alpha=0.2, color='r', label='Volume')
ax2.set_ylabel('Volume', color='r')
plt.title('AAPL: Close Price and Volume (1 year)')
plt.show()
No description has been provided for this image

Advanced Example 3: Rolling Statistics for Volatility#

  • Financial volatility is often measured with rolling averages or standard deviations.
  • A rolling standard deviation of returns helps spot calm versus turbulent periods.
# Compute 20-day rolling standard deviation of returns for MSFT
msft = ohlcv[ohlcv['Ticker'] == 'MSFT'].copy()
msft['RollStd'] = msft['Return'].rolling(20).std()
print(msft[['Date','Return','RollStd']].tail(5))
           Date    Return   RollStd
1233 2026-07-07  0.543002  2.401126
1238 2026-07-08 -1.414464  2.406064
1243 2026-07-09  0.266079  2.375508
1248 2026-07-10  0.192533  2.357406
1253 2026-07-13  1.529469  2.352200

Error Handling: Missing Data and NaNs in OHLCV#

  • Stock markets do not trade on weekends and some days have missing data.
  • OHLCV manipulations can introduce NaN values.
  • It is important to detect and handle missing values to avoid incorrect results.
# Count NaNs in each column after all calculations so far
na_counts = ohlcv.isna().sum()
print('Missing values per column:')
print(na_counts)
Missing values per column:
Date      0
Ticker    0
Close     0
High      0
Low       0
Open      0
Volume    0
Return    5
dtype: int64
# Drop rows with any missing data (if desired)
clean_ohlcv = ohlcv.dropna()
print('OHLCV after dropping missing:', clean_ohlcv.shape)
OHLCV after dropping missing: (1250, 8)

Common Best Practices for OHLCV Data in Analytics#

  • Always check for missing or duplicate data before analysis.
  • Use clear column names and document your data structures.
  • Keep your code modular and reusable for other tickers or new data.
  • Set random seed for reproducibility (e.g. np.random.seed(42)).
# Check for duplicate Date/Ticker pairs
dupes = ohlcv.duplicated(subset=['Date','Ticker']).sum()
print(f'Total duplicate date/ticker rows: {dupes}')
Total duplicate date/ticker rows: 0

End-to-End Example: Identify Best-Performing Stock Over the Year#

  • Let us combine our skills: load, clean, pivot, compute returns, and find the winner.
  • We want to know which of our five tech stocks achieved the highest total return.
# Step 1: Clean OHLCV DataFrame
df = ohlcv.dropna()
# Step 2: Pivot to wide Close price table
wide = df.pivot(index='Date', columns='Ticker', values='Close')
# Step 3: Calculate total return for each ticker (percent change from start to end)
total_ret = 100.0 * (wide.iloc[-1] / wide.iloc[0] - 1)
best = total_ret.idxmax()
print('Total return by ticker (%):')
print(total_ret.round(2))
print(f'Best performing stock: {best}')
Total return by ticker (%):
Ticker
AAPL     52.35
AMZN      9.26
GOOGL    94.24
MSFT    -22.08
TSLA     27.02
dtype: float64
Best performing stock: GOOGL
filename = 'best_stock_report.txt'
with open(filename, 'w') as f:
    f.write('Total return by ticker (%)\n')
    for ticker, val in total_ret.round(2).items():
        f.write(f'{ticker}: {val}%\n')
    f.write(f'\nBest performing stock: {best}\n')
print(f'Results saved to {filename}')
Results saved to best_stock_report.txt

Congratulations! You now understand OHLCV data and how to analyze it for finance and stock market analytics.#

  • Review and practice filtering, pivoting, and return calculations using real OHLCV data.
  • Try more advanced analytics or subscribe to our YouTube channel for deeper tutorials on stock data.

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.