Introduction
I recommend you take a look at the interactive version using Marimo to get an idea of the output.
I’ve been using Python for some time now and have built applications and personal automation scripts with it. I’ve also done general data analysis using its specialized libraries like pandas.
By utilizing Python’s various libraries, we can retrieve financial and market data, manipulate that data, and perform analyses based on statistical methods and visualizations.
One of the projects I’m currently working on is an attempt at aggregating this information and displaying it in an efficient interface, using the same Python packages mentioned below.
Python Packages
- yfinance - financial and market data
- pandas - data manipulation and analysis
- matplotlib - visualizations
- numpy - scientific computing
- statsmodels - statistical data exploration
# It's common to use these aliases when importing
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import numpy as np
import statsmodels.api as sm
Financial Statements
- Income (P&L) Statement
- Balance Sheet
- Cash Flow Statement
amzn = yf.Ticker("AMZN") # using Amazon as the company
amzn.income_stmt
amzn.balance_sheet
amzn.cash_flow
This is mostly self-explanatory. You select the ticker and retrieve the corresponding annual financial document(s). You can prepend quarterly (e.g., quarterly_income_stmt) to get quarterly data instead. We will use values in these documents to calculate some financial ratios in a later section.
Company Information
amzn.info # returns data in JSON format
This returns information about the company, including industry and sector classification, company officers, and much more. However, because this information is serialized, we want to convert it into a easier format data manipulation. We can do this by converting the data into a pandas Series object as in the code below.
def get_metrics(ticker_name):
# Convert to Pandas Series for easy data manipulation
ticker = yf.Ticker(ticker_name)
ticker_info = pd.Series(ticker.info)
rename_map = {
"open": "Open",
"dayHigh": "High",
"dayLow": "Low",
"volume": "Volume",
"trailingPE": "Trailing P/E",
"forwardPE": "Forward P/E",
"marketCap": "Market Cap",
"fiftyTwoWeekHigh": "52-Week High",
"fiftyTwoWeekLow": "52-Week Low",
"averageVolume": "Average Volume",
"dividendYield": "Yield",
"beta": "Beta",
"trailingEps": "Trailing EPS",
}
metrics = rename_map.keys()
return (
ticker_info.reindex(metrics) # handle missing values
.rename(rename_map)
.fillna("N/A")
.map(format_finance) # format large numbers
.to_frame("Value")
.rename_axis("Metric")
)
get_metrics("AMZN")
I decided to make this a function for reusability, allowing me to plug in any ticker to get the corresponding company’s information. The code above basically extracts and renames fields found in the JSON data.
def format_finance(value):
if not isinstance(value, (int, float)) or value == 0:
return value
abs_val = abs(value) # properly handle negative values
if abs_val >= 1_000_000_000_000:
return f"{value / 1_000_000_000_000:.2f}T"
if abs_val >= 1_000_000_000:
return f"{value / 1_000_000_000:.2f}B"
if abs_val >= 1_000_000:
return f"{value / 1_000_000:.2f}M"
return f"{value:,.2f}"
This is the function I used to make larger numbers more readable.
Financial Ratios
Financial ratios are an important and quick way to evaluate a company’s performance and standing. They can be obtained through the financial statements, and by extension yfinance, either directly or indirectly.
Some examples of ratios are readily available through yfinance include:
- PEG Ratio (Trailing)
- Growth rate should be roughly equal to the P/E ratio
- Current Ratio
- Firm’s ability to meet short-term obligations through its current assets
- Return on Equity
ratios = ["trailingPegRatio", "currentRatio", "returnOnEquity"]
pd.Series(amzn.info)[ratios]
While others require calculation.
- FCF (Free Cash Flow) Yield
- Cash flow available for spending relative to the market cap
# Calculating FCF (OCF - CapEx)
amzn_cf = amzn.cash_flow
ocf = amzn_cf.loc["Operating Cash Flow"]
capex = amzn_cf.loc["Capital Expenditure"]
fcf = ocf - capex
# Calculating FCF Yield (FCF / Market Cap)
market_cap = amzn.info.get("marketCap")
fcf_yield = fcf / market_cap
- DuPont Formula (ROE)
- Profit Margin Asset Turnover Financial Leverage
- ROE is available above but this breaks it into its constituent parts
# DuPont formula: profit margin * asset turnover * financial leverage
amzn_pnl = amzn.income_stmt
amzn_bs = amzn.balance_sheet
# profit margin = net income / sales
net_income = amzn_pnl.loc["Net Income"]
sales = amzn_pnl.loc["Total Revenue"]
profit_margin = net_income / sales
# asset turnover = sales / assets
assets = amzn_bs.loc["Total Assets"]
asset_turnover = sales / assets
# financial leverage = assets / equity
equity = amzn_bs.loc["Stockholders Equity"]
leverage = assets / equity
roe = profit_margin * asset_turnover * leverage
- Return on Invested Capital (ROIC)
- How much income a company earns per dollar invested
# ROIC = NOPAT / invested capital
# NOPAT = EBIT * (1 - tax rate)
ebit = amzn_pnl.loc["EBIT"]
tax_provision = amzn_pnl.loc["Tax Provision"]
pretax_income = amzn_pnl.loc["Pretax Income"]
tax_rate = tax_provision / pretax_income
nopat = ebit * (1 - tax_rate)
# invested capital = (debt + equity) - cash
cash = amzn_bs.loc["Cash And Cash Equivalents"]
equity = amzn_bs.loc["Shareholders Equity"]
debt = amzn_bs.loc["Total Debt"]
invested_capital = (debt + equity) - cash
roic = nopat / invested_capital
Historical Stock Data
historical_data = yf.Ticker("AMZN").history(start="2021-01-01", end="2025-12-31")
historical_data
Here, we get historical trading data from 2021 to 2025.
def plot_closing_prices(data):
close_prices = data["Close"]
close_prices.plot(figsize=(12, 8), fontsize=12)
plt.ylabel("Price")
plt.title(f"Price Chart", fontsize=15)
plt.show()
plot_closing_prices(amzn_historical_data)
The above code displays a historical stock price chart for Amazon based on the provided historical trading data.
Note that the code in the Marimo notebook is a bit different since plt.show() only displays code in the console. If you want to display it in a Marimo app, you have to do this.
def plot_closing_prices(data):
amzn_goog_nvda_historical_data = yf.Tickers(["AMZN", "GOOG", "NVDA"]).history(
start="2021-01-01", end="2025-12-31"
)
plot_closing_prices(amzn_goog_nvda_historical_data)
You can also get historical data for multiple tickers at once. By doing so, you can display stock information for multiple companies in the same graph for comparison.
def plot_closing_prices_percentage(data):
close_prices = data["Close"]
price_percent_change = (close_prices / close_prices.iloc[0]) * 100
price_percent_change.plot(figsize=(12, 8), fontsize=12)
plt.ylabel("Percentage")
plt.title(f"Price Chart", fontsize=15)
plt.show()
plot_closing_prices_percentage(amzn_goog_nvda_historical_data)
Of course, you can also change how you want to display the information on the chart. For example, we plot the percentage change in the price instead of the stock price. Doing this highlights the explosive growth of Nvidia.