Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
The original Yahoo Finance example built around pandas-datareader is now a legacy pattern. For a current beginner workflow, use yfinance to download historical stock prices, pandas to clean and analyze them, and Plotly to build interactive charts. You can still use pandas-datareader for maintained sources such as FRED and Fama/French.
This tutorial builds a notebook that downloads several stocks, validates the resulting DataFrame, calculates returns, moving averages and drawdowns, compares performance fairly, and creates line and candlestick charts.
What you will build
By the end, you will have:
- A historical price dataset for Google, Amazon, Microsoft, Apple and Meta.
- Basic descriptive statistics and daily returns.
- Normalized performance comparisons starting at 100.
- Moving-average and drawdown calculations.
- Interactive Plotly line, faceted and candlestick charts.
- A practical understanding of when to use
yfinanceversuspandas-datareader.
The examples analyze historical data only. They are not real-time market feeds, trading signals or investment advice.
Recommended Free Tools
What each library does
- pandas supplies DataFrames, indexes, joins, rolling calculations and missing-data tools.
- pandas-datareader is a connector for supported economic, policy, central-bank and factor datasets. It is not a guarantee that every historical provider still works.
- yfinance downloads Yahoo-sourced historical market data through an independent open-source project. It is not an official Yahoo SDK. See the project notes and data-use warning.
- Plotly Express is the high-level charting API for quick line and comparison charts.
- Plotly Graph Objects provides lower-level control for candlesticks, multiple traces, overlays and customized axes.
Plotly’s line-chart documentation describes this distinction between Plotly Express and Graph Objects. Cufflinks, a legacy pandas-to-Plotly bridge used in some older tutorials, is unnecessary here.
#1 Best Overall
- Language: english
- Book - trading: technical analysis masterclass: master the financial markets
- It is made up of premium quality material.
Install the environment
Create an isolated virtual environment:
python -m venv .venv
Activate it on macOS or Linux:
source .venv/bin/activate
On Windows PowerShell:
.venvScriptsActivate.ps1
Install the packages used by the current stock-analysis workflow:
python -m pip install --upgrade pip
python -m pip install pandas yfinance plotly jupyterlab
Install pandas-datareader only if you also want the FRED or Fama/French examples:
python -m pip install pandas-datareader
Versions checked August 18, 2026 were pandas-datareader 0.11.1, yfinance 1.6.0 and Plotly 6.9.0. These versions will change. The pandas-datareader 0.11.1 release requires Python 3.11 or newer, while Plotly 6.9.0 requires Python 3.8 or newer. Check the current pandas-datareader, yfinance and Plotly package pages before creating a new environment.
For a published notebook, record the installed versions:
python -m pip freeze > requirements-lock.txt
The old DataReader approach—and why it may fail
Older tutorials commonly used code like this:
import pandas_datareader.data as data
df = data.DataReader(
"AAPL",
"yahoo",
start="2021-01-01",
end="2026-01-01",
)
This is the pattern used by the matching 2021 tutorial, which also used the old FB symbol and Cufflinks. It may now fail because current pandas-datareader documentation focuses on maintained macroeconomic, policy, central-bank and factor sources; Yahoo is not presented as a maintained public reader.
That does not make the original teaching idea wrong. It means the data-access layer should be updated. Use yfinance for this stock-price tutorial and reserve pandas-datareader for sources it currently documents.
Download historical stock prices with yfinance
import yfinance as yf
tickers = ["GOOG", "AMZN", "MSFT", "AAPL", "META"]
prices = yf.download(
tickers=tickers,
start="2021-01-01",
end="2026-01-01",
auto_adjust=False,
progress=False,
)
prices.head()
META is the current ticker for Meta Platforms. New code should not use the former FB symbol.
The end value is commonly treated as an exclusive boundary. Always inspect the returned final date rather than assuming that January 1, 2026 is included. Also remember that this is historical data. It may be delayed, incomplete, rate-limited or affected by provider changes; it should not be described as a guaranteed real-time feed.
Validate the download immediately
print(prices.columns)
print(prices.columns.names)
print(prices.index.dtype)
print(prices.shape)
print(prices.isna().sum())
print(prices.index.min(), prices.index.max())
These checks catch empty downloads, unexpected column layouts, missing history and date-range mistakes before they contaminate later calculations.
Adjusted and unadjusted prices
With auto_adjust=False, the returned data can include raw Open, High, Low and Close values plus separate adjustment-related fields such as adjusted close, dividends or splits, depending on the response.
Use auto_adjust=True when the goal is a straightforward historical comparison that accounts for the provider’s adjustment treatment. Use auto_adjust=False when you need to teach or inspect raw OHLC values, dividends and split adjustments separately.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Do not mix adjusted close with raw Open, High and Low in the same candlestick. An OHLC chart should use one consistent adjustment regime and one consistent security series.
Understand the DataFrame and its MultiIndex
When downloading multiple tickers, yfinance commonly returns hierarchical columns. The hierarchy may be arranged as (Price, Ticker) or (Ticker, Price), depending on library behavior and options. A MultiIndex is not an error: it represents more than one dimension in the columns.
Never assume the order. Inspect it:
print(prices.columns.names)
print(prices.columns)
If the first level contains fields such as Close, select the close prices like this:
close = prices["Close"].copy()
If the second level contains the fields, use a cross-section:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
close = prices.xs("Close", axis=1, level=1).copy()
The method xs() means cross-section selection: it selects one value from a particular MultiIndex level.
A defensive helper works with either common layout:
def extract_field(data, field):
if not hasattr(data.columns, "levels"):
return data[[field]].copy()
if field in data.columns.get_level_values(0):
return data[field].copy()
if field in data.columns.get_level_values(1):
return data.xs(field, axis=1, level=1).copy()
raise KeyError(f"{field!r} not found in columns")
close = extract_field(prices, "Close")
close = close.sort_index()
close = close.dropna(how="all")
close.head()
Keeping the MultiIndex is usually safer for a larger analysis. Flattening can make beginner code easier, but it can also hide whether a column means Close_AAPL or AAPL_Close:
Rank #3
- As a day trader, you can live and work anywhere in the world. You can decide when to work and when not to work.
- You only answer to yourself. That is the life of the successful day trader. Many people aspire to it, but very few succeed. Day trading is not gambling or an online poker game.
- To be successful at day trading you need the right tools and you need to be motivated, to work hard, and to persevere.
if hasattr(prices.columns, "levels"):
prices.columns = [
"_".join(str(part) for part in column).strip()
for column in prices.columns.to_flat_index()
]
Analyze one stock with pandas
Descriptive statistics
close.describe()
This reports observations such as count, mean, standard deviation, minimum, quartiles and maximum. It describes the downloaded sample; it does not establish which company is a better investment.
Daily returns
daily_returns = close.pct_change().dropna()
daily_returns.head()
A return is the percentage change from one available observation to the next. It is generally more useful for comparing securities than their nominal quoted prices.
Cumulative growth of one dollar
growth = (1 + daily_returns).cumprod()
growth.tail()
This shows how a hypothetical one-unit investment would have grown if the calculated returns were compounded. It is a mathematical illustration, not a claim that the result includes every dividend, fee, tax or execution effect.
Period return
period_return = close.iloc[-1] / close.iloc[0] - 1
print(period_return)
This is a simple price-based period return. If you need a total-return comparison, use an appropriately adjusted series and state exactly how dividends and splits were treated.
Moving averages
moving_average_20 = close.rolling(20).mean()
moving_average_50 = close.rolling(50).mean()
The first rows are naturally missing because a 20- or 50-observation window is not yet available. A moving average summarizes recent prices; it does not predict the next price.
Drawdown
wealth = (1 + daily_returns).cumprod()
running_peak = wealth.cummax()
drawdown = wealth / running_peak - 1
drawdown.min()
Drawdown measures the decline from the previous running peak. It complements return and volatility by showing how deep a historical decline became.
Annualized volatility
annualized_volatility = daily_returns.std() * (252 ** 0.5)
print(annualized_volatility)
This approximation uses daily returns and the conventional estimate of 252 U.S. trading sessions per year. The number is not universal; label the frequency and convention whenever you report it. Volatility is not the same as risk-adjusted performance and does not account for an investor’s goals, liabilities or time horizon.
Compare stocks fairly: normalize the starting point
A raw-price chart can mislead. A $500 stock is not automatically outperforming a $50 stock, because share prices depend on splits, share structure and company history.
normalized = close.div(close.iloc[0]).mul(100)
normalized.head()
Every series now starts at 100. The endpoint answers: “What would the starting index be worth under this price series?” It does not automatically include dividends or fees.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #4
- Used Book in Good Condition
You can also compare cumulative returns:
cumulative_returns = (1 + daily_returns).cumprod() - 1
cumulative_returns.tail()
Any statement such as “the best-performing stock” must be limited to the selected dates, price basis, adjustment method and dataset. Historical outperformance is not a forecast.
Create interactive Plotly line charts
One stock
import plotly.express as px
ticker = "AAPL"
fig = px.line(
close,
x=close.index,
y=ticker,
title=f"{ticker} closing price",
labels={"x": "Date", ticker: "Price"},
)
fig.update_layout(hovermode="x unified")
fig.show()
Plotly connects points in the order supplied. Sort time-series data before charting:
close = close.sort_index()
Otherwise, an unsorted index can produce a line that appears to move backward in time.
Several stocks on one normalized chart
fig = px.line(
normalized,
x=normalized.index,
y=normalized.columns,
title="Normalized stock performance",
labels={
"value": "Indexed value (start = 100)",
"variable": "Ticker",
},
)
fig.update_layout(hovermode="x unified")
fig.show()
Hovering over a date displays the series together, which makes the chart easier to inspect than separate static images.
Faceted charts
Facets give each ticker its own panel. This is useful when a combined chart becomes visually crowded or when nominal price levels matter.
long_close = (
close.reset_index()
.rename(columns={"index": "Date"})
.melt(
id_vars="Date",
var_name="Ticker",
value_name="Close",
)
.dropna()
)
fig = px.line(
long_close,
x="Date",
y="Close",
facet_col="Ticker",
facet_col_wrap=2,
title="Closing prices by ticker",
)
fig.show()
Build a candlestick chart
A candlestick represents one security over each time interval:
- The body spans the opening and closing prices.
- The wick, or shadow, spans the interval’s high and low.
- Color is a display convention for the relationship between open and close; it is not itself a trading signal.
First extract one ticker’s OHLC data. The exact selection depends on the MultiIndex order, so inspect the columns first. If tickers are the second level:
aapl = prices.xs("AAPL", axis=1, level=1)
required = {"Open", "High", "Low", "Close"}
missing = required - set(aapl.columns)
if missing:
raise ValueError(f"Missing OHLC fields: {missing}")
Then plot it with Graph Objects:
import plotly.graph_objects as go
fig = go.Figure(
data=[
go.Candlestick(
x=aapl.index,
open=aapl["Open"],
high=aapl["High"],
low=aapl["Low"],
close=aapl["Close"],
name="AAPL",
)
]
)
fig.update_layout(
title="AAPL candlestick chart",
xaxis_rangeslider_visible=False,
yaxis_title="Price",
)
fig.show()
See Plotly’s candlestick API documentation for the dedicated chart type.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Candlesticks show what happened during each interval; they do not predict what happens next. The chart can also be misleading if dates are missing, OHLC values are inconsistent, or adjusted and unadjusted fields are mixed.
Best Value
Add a moving-average overlay
aapl_close = close["AAPL"]
fig = go.Figure()
fig.add_trace(
go.Scatter(
x=aapl_close.index,
y=aapl_close,
mode="lines",
name="Close",
)
)
fig.add_trace(
go.Scatter(
x=aapl_close.index,
y=aapl_close.rolling(50).mean(),
mode="lines",
name="50-day moving average",
)
)
fig.update_layout(
title="AAPL close and 50-day moving average",
hovermode="x unified",
)
fig.show()
Add volume carefully
volume = extract_field(prices, "Volume")
volume.head()
Volume is a separate series and may have missing values or provider-specific conventions. If you combine it with price in a custom figure, verify that both series share the same dates and ticker and that the chart’s axes make the units clear.
Use pandas-datareader where it is currently maintained
pandas-datareader remains useful for economic and factor data. For example, retrieve the U.S. 10-year Treasury constant-maturity rate from FRED:
import pandas_datareader.data as web
fred = web.DataReader(
"DGS10",
"fred",
start="2021-01-01",
end="2026-01-01",
)
fred.head()
For Fama/French factor data:
from pandas_datareader import data as web
factors = web.DataReader(
"F-F_Research_Data_Factors",
"famafrench",
start="2021-01-01",
end="2026-01-01",
)
factors[0].head()
The current remote-data documentation identifies FRED and Fama/French among its documented sources and describes the project’s focus on macroeconomic, policy, central-bank and factor-style data.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesTroubleshooting
“DataReader(…, ‘yahoo’, …)” fails
Install yfinance and replace the retrieval layer:
python -m pip install yfinance
import yfinance as yf
df = yf.download(
"AAPL",
start="2021-01-01",
end="2026-01-01",
progress=False,
)
FB returns no data
Use META in new examples. Ticker symbols can change, so old notebooks often require updates even when the analysis itself is sound.
MultiIndex selection raises KeyError
print(df.columns)
print(df.columns.names)
Select the field by the level where it actually appears, or use the defensive extract_field() helper above.
Missing values appear
Possible causes include market holidays, different trading calendars, a ticker with shorter history, temporary provider failures, delistings, renamed securities or dates outside an instrument’s available history. Do not blindly forward-fill OHLC data. Investigate missing observations before calculating returns.
A Plotly chart does not render
Try the normal display call:
fig.show()
If your notebook needs an explicit renderer:
import plotly.io as pio
pio.renderers.default = "notebook_connected"
To open figures in a local browser:
pio.renderers.default = "browser"
A candlestick looks wrong
aapl[["Open", "High", "Low", "Close"]].dtypes
aapl[["Open", "High", "Low", "Close"]].isna().sum()
Confirm that the index is datetime-like and that all four fields come from the same ticker and adjustment regime.
Recommended Free Tools
Data limitations and responsible interpretation
This notebook describes a selected historical window. It does not account automatically for:
- Dividends, fees, taxes or trading costs.
- Corporate-action treatment beyond the selected adjustment setting.
- Survivorship bias from analyzing securities that remain available today.
- Look-ahead bias in any future backtest.
- Different exchange calendars and missing observations.
- Provider outages, rate limits or changes to public endpoints.
“Best performing” therefore needs a precise definition: dates, price or total-return basis, adjustment method, benchmark and risk measure. A higher return may have come with a much larger drawdown or volatility.
yfinance is suitable for educational, exploratory and personal analysis, but its project page describes it as independent from Yahoo and warns that data access is subject to applicable terms. It should not be treated as a production market-data contract for regulated systems, commercial redistribution or latency-sensitive trading.
Where to run the notebook
Local JupyterLab gives you the most control and makes environment pinning straightforward. Google Colab is a convenient browser-based option for beginners who do not want to configure Python locally. Kaggle Notebooks can be useful for public datasets and shareable educational work, but neither hosted environment should be assumed to provide persistent, private or production-grade execution.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Useful next steps
- Compare each stock with a relevant benchmark index.
- Calculate rolling volatility and correlations.
- Use dividend-aware total-return data when that is the analytical goal.
- Build a portfolio with explicit weights and rebalancing rules.
- Backtest only with strict controls against look-ahead and survivorship bias.
- Turn the figures into a Dash application. Plotly’s documentation points to Dash for analytical web apps.
- Use a licensed commercial API when you need contractual uptime, defined corporate-action handling, redistribution rights or intraday access.
For readers who want to publish or privately share a dashboard, Plotly Cloud or Plotly Studio may be relevant. Pricing seen August 18, 2026 listed a free plan and a Pro plan at $29 per creator seat per month or $290 annually, but limits and prices change; verify the current official pricing page before purchasing. Local Plotly charts do not require Plotly Cloud.
Quick Recap
Choosing a data source
| Goal | Practical choice | Main qualification |
|---|---|---|
| Beginner historical stock exploration | yfinance | Independent project and public-provider terms apply. |
| Interest rates and macroeconomic series | pandas-datareader with FRED | Use the supported source and inspect its frequency and missing dates. |
| Academic factor research | pandas-datareader with Fama/French | Understand the dataset’s frequency, definitions and factor conventions. |
| Production or commercial distribution | A licensed direct market-data provider | Compare coverage, quotas, adjustments, rights, support and SLA. |
| Fixed reproducible tutorial data | A versioned CSV file | Document its origin, download date and adjustment method. |
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

