Build a Stock Price Analyzer

Write Python from scratch to fetch real gold prices and generate buy/hold/sell signals.

Introduction

30 Second Summary

Every day, gold and stock prices move up and down. Knowing when to buy, hold, or sell can feel like guesswork without the right data.

In this project, you will write a Python script that pulls real gold price data from the internet and calculates whether the trend says buy, hold, or sell.

What You'll Build

A command-line investment analyzer that fetches live market data for gold and prints a formatted report with price trends, moving averages, and a trading signal.

By the end of this project, you'll have:

  • A working Python script called stock_analyzer.py that fetches real gold price data from Yahoo Finance.
  • Investment metrics (moving averages and daily returns) calculated with functions you wrote from scratch.
  • A buy/hold/sell signal generator based on a real trading strategy called moving average crossover.
  • Secret Mission: a multi-asset comparison tool that ranks gold, silver, and stocks by performance.

Are there any prerequisites?

None. This guide includes setup for all required tools. You just need a Windows computer with Chrome installed.

Set Up Your Python Environment

Before writing any code, you need a working environment where Python can run your scripts and download external libraries.

In this step, you will verify that Python and pip (Python's package installer) are ready to go, install a financial data library, and create the project folder where your analyzer will live.

In this step, get ready to:
  • Verify Python and pip are working in your terminal.
  • Install the yfinance library for fetching market data.
  • Create a project folder and open it in Cursor.
Verify Python and pip

You already have Python installed, but let's confirm your terminal can find it.

  • Press the Windows key to open your search bar.
  • Type cmd and press Enter to open Command Prompt.
  • Check your Python version by running this command:
python --version
  • Check your pip version by running:
pip --version

You should see Python 3.14.x and a pip version number printed in the terminal.

Command not found?

If python does not work, try py instead. Windows sometimes registers the Python executable under py.

If neither works, Python may not be added to your system PATH. Reinstall Python from python.org and make sure to check the Add Python to PATH box during installation.

help me fix Python not being recognized in my Windows CMD

Install the yfinance library

The yfinance library lets you pull real stock and commodity prices from Yahoo Finance with just a few lines of code.

  • Install yfinance by running this command in your CMD window:
pip install yfinance

What does this command do?

pip is Python's package manager. It downloads yfinance and all of its dependencies (including pandas and numpy) from the internet and installs them on your machine.

You will see output showing packages being downloaded. This may take a minute depending on your connection speed.

  • Confirm yfinance installed correctly by running:
python -c "import yfinance; print(yfinance.__version__)"

You should see a version number like 1.4.1 printed in the terminal.

Seeing an error during install?

If you see a permissions error, try running CMD as Administrator. Right-click Command Prompt in your search results and select Run as administrator.

If you see a connection timeout, check your internet connection and try again.

help me fix a pip install error on Windows

Create your project folder and file

Now you need a dedicated folder for your project files.

  • Move to your Desktop by running this command:
cd %USERPROFILE%\Desktop
  • Create your project folder and move into it by running these commands:
mkdir stock-analyzer
cd stock-analyzer
  • Confirm the folder was created by running:
cd

You should see a path ending in Desktop\stock-analyzer printed in the terminal.

  • Open this folder in Cursor by running:
cursor . --classic

Why --classic?

Cursor's default window is the Agents Window, which is designed for AI-driven coding. The --classic flag opens the traditional Editor window instead, which is what you need for writing code by hand.

  • In the Cursor window that opens, right-click in the Explorer sidebar on the left.
  • Select New File.
  • Type stock_analyzer.py and press Enter.

You should see stock_analyzer.py appear in the sidebar and an empty editor tab open for it.

Cursor didn't open?

If the cursor command is not recognized, open Cursor manually. Press the Windows key, type Cursor, and press Enter. Then click File in the top menu, click Open Folder, and navigate to Desktop > stock-analyzer.

help me open my project folder in Cursor on Windows

Your environment is ready. Next up, you will fetch real gold price data from Yahoo Finance and see live market numbers in your terminal.

Fetch Real Gold Price Data

Your environment is ready to go. Now it's time to write real Python code that connects to the internet and fetches live market data.

In this step, you'll use the yfinance library to pull one month of gold futures prices from Yahoo Finance. By the end, you'll have actual gold prices stored in a Python list, ready for analysis.

In this step, get ready to:
  • Write Python code that fetches gold price data from Yahoo Finance.
  • Run your script and see raw market data in your terminal.
  • Extract closing prices into a Python list for analysis.
Write your first lines of Python

Your stock_analyzer.py file is currently empty. You'll add code that imports the yfinance library and uses it to download one month of gold futures prices.

  • In Cursor, open stock_analyzer.py and add the following code:
# Load the yfinance library
import yfinance as yf

# Create a Ticker object for Gold Futures
gold = yf.Ticker("GC=F")
# Fetch the last month of daily price data
data = gold.history(period="1mo")
# Display the raw data table
print(data)

What does this code do?

  • import yfinance as yf loads the yfinance library and gives it the shorthand name yf so you can type less.
  • yf.Ticker("GC=F") creates a Ticker object for Gold Futures. GC=F is the Yahoo Finance symbol for COMEX gold.
  • .history(period="1mo") fetches the last one month of daily price data and returns it as a table.
  • print(data) displays the entire raw data table in your terminal so you can see what you're working with.
  • Save stock_analyzer.py.
Run your script
  • In your CMD window, run your script with this command:
python stock_analyzer.py

You should see a table with columns including Open, High, Low, Close, and Volume. Each row is one trading day of gold prices in USD per ounce.

What should I see?

The table shows roughly 20-22 rows (one per trading day in the past month). Weekends and holidays have no rows because markets are closed on those days.

If you run this on a weekend, the most recent row will be from Friday. This is normal.

Seeing an error instead of data?

  • If you see ModuleNotFoundError: No module named 'yfinance', pip may have installed yfinance to a different Python version. Run pip install yfinance again in the same CMD window.
  • If you see No data found, your internet connection may be down or Yahoo Finance may be temporarily unavailable. Check your connection and try again.
  • If python is not recognized, try using py stock_analyzer.py instead.

Help me debug why my Python script isn't fetching gold data.

Store prices in a list

The raw table has more information than you need. For your analysis, you only need the closing price from each trading day.

  • In stock_analyzer.py, find this line:
print(data)
  • Replace that entire line with the following code:
# Extract just the closing prices into a Python list
close_prices = data["Close"].tolist()

# Display summary of the price data
print(f"Number of trading days: {len(close_prices)}")
print(f"Most recent gold price: ${close_prices[-1]:.2f}")
print(f"Oldest gold price: ${close_prices[0]:.2f}")

What does this code do?

  • data["Close"].tolist() extracts just the closing prices from the table and converts them into a plain Python list.
  • len(close_prices) counts how many trading days are in the list.
  • close_prices[-1] gets the last item in the list, which is the most recent price. Negative indexing in Python counts from the end.
  • close_prices[0] gets the first item, which is the oldest price in the month.
  • :.2f inside the f-string formats the number to exactly 2 decimal places.
  • Save stock_analyzer.py.
  • Run your script again with this command:
python stock_analyzer.py

You should see three lines showing the number of trading days, the most recent gold price, and the oldest gold price in the month.

Not seeing the expected output?

  • If you still see the full data table, make sure you deleted the original print(data) line. It should be completely removed and replaced with the new code.
  • If you see a KeyError, check that you typed "Close" with a capital C. Column names are case-sensitive.
  • If you see both the table AND the three summary lines, you have print(data) still in your file above the new code. Delete it.

Help me fix my gold price output in Python.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before moving on.

ⓧ I'd like to double check the full code

Here's the complete stock_analyzer.py at this point:

import yfinance as yf

gold = yf.Ticker("GC=F")
data = gold.history(period="1mo")
close_prices = data["Close"].tolist()

print(f"Number of trading days: {len(close_prices)}")
print(f"Most recent gold price: ${close_prices[-1]:.2f}")
print(f"Oldest gold price: ${close_prices[0]:.2f}")

You now have real gold price data flowing into your script. Next up, you'll write functions that calculate investment metrics from these prices.

Calculate Investment Metrics

In the previous step, you fetched real gold price data from Yahoo Finance and stored the closing prices in a Python list. Now you have raw numbers, but raw numbers alone don't tell you whether gold is trending up or down.

In this step, you'll write Python functions that transform raw prices into meaningful investment metrics. You'll calculate daily returns (how much the price changes each day) and moving averages (smoothed price trends over time).

In this step, get ready to:
  • Calculate daily returns as percentages.
  • Calculate a simple moving average.
  • Print the computed investment metrics.
Calculate daily returns

A daily return measures how much an asset's price changed from one day to the next, expressed as a percentage. If gold was $2,300 yesterday and $2,323 today, that's a 1% daily return.

You'll write a function that loops through your list of prices and calculates this percentage for every pair of consecutive days.

  • In stock_analyzer.py, find the line gold = yf.Ticker("GC=F").
  • Add the following function above that line, leaving a blank line between the function and the gold line:
def calculate_daily_returns(prices):
    # Calculate percentage change between each consecutive day
    returns = []
    for i in range(1, len(prices)):
        daily_return = (prices[i] - prices[i - 1]) / prices[i - 1] * 100
        returns.append(daily_return)
    return returns

What does this code do?

This function takes a list of prices and returns a list of daily percentage changes.

  • def calculate_daily_returns(prices): defines a reusable function that accepts any list of prices as input.
  • for i in range(1, len(prices)): loops through every day starting from the second one (index 1), because you need a "previous day" to compare against.
  • The formula (prices[i] - prices[i - 1]) / prices[i - 1] * 100 calculates the percentage change from one day to the next.
  • returns.append(daily_return) adds each calculated return to the list.
  • return returns sends the completed list back to whoever called the function.
  • Save stock_analyzer.py.

You won't see a change yet. The function is defined but not called. The next chunk uses it.

Calculate a moving average

A moving average smooths out daily price noise by averaging the last N days of prices. A 5-day moving average reacts quickly to recent changes, while a 20-day moving average shows the broader trend.

  • In stock_analyzer.py, add the following function below calculate_daily_returns and above gold = yf.Ticker("GC=F"):
def calculate_moving_average(prices, window):
    # Return the average of the last 'window' prices
    if len(prices) < window:
        return None
    recent_prices = prices[-window:]
    average = sum(recent_prices) / len(recent_prices)
    return average

What does this code do?

This function calculates a Simple Moving Average (SMA) over a specified number of days.

  • def calculate_moving_average(prices, window): accepts both the price list and a window parameter (how many days to average).
  • if len(prices) < window: return None is a safety check. If you only have 3 days of data, you can't calculate a 20-day average.
  • prices[-window:] uses list slicing with a negative index to grab the last N prices from the list.
  • sum(recent_prices) / len(recent_prices) calculates the average using Python's built-in sum() function.
  • Save stock_analyzer.py.

You won't see a change yet. The next chunk calls both functions and prints the results.

Call your functions and print results

Now you'll use both functions to compute actual investment metrics from your gold price data and print them.

  • At the bottom of stock_analyzer.py, below the existing print statements about trading days, add the following code:
daily_returns = calculate_daily_returns(close_prices)
avg_return = sum(daily_returns) / len(daily_returns)
sma_5 = calculate_moving_average(close_prices, 5)
sma_20 = calculate_moving_average(close_prices, 20)

print(f"Average daily return: {avg_return:.2f}%")
print(f"5-day moving average: ${sma_5:.2f}")
print(f"20-day moving average: ${sma_20:.2f}")

What does this code do?

  • calculate_daily_returns(close_prices) passes your gold closing prices into the function you wrote, getting back a list of daily percentage changes.
  • sum(daily_returns) / len(daily_returns) calculates the average of all daily returns. This tells you how much gold moves on a typical day.
  • calculate_moving_average(close_prices, 5) gets the 5-day short-term trend, while passing 20 gets the longer-term trend.
  • The :.2f in the f-strings formats numbers to exactly 2 decimal places.
  • Save stock_analyzer.py.
  • Run the script by entering this command in CMD:
python stock_analyzer.py
  • You should see the original three lines of output, plus three new lines showing your average daily return (a small percentage), the 5-day moving average, and the 20-day moving average (both in dollars).

Seeing a NameError?

  • If you see NameError: name 'calculate_daily_returns' is not defined, make sure your functions are placed above the gold = yf.Ticker("GC=F") line. Python reads top to bottom, so functions must be defined before they are called.
  • If you see IndentationError, check that every line inside a function is indented with exactly 4 spaces. Mixing tabs and spaces causes this error.

Why isn't my daily returns function working?

✔️ Awesome, I've got everything!

Great. Double check you've saved your file.

ⓧ I'd like to double check the full code

Here's what your complete stock_analyzer.py should look like at this point:

import yfinance as yf


def calculate_daily_returns(prices):
    returns = []
    for i in range(1, len(prices)):
        daily_return = (prices[i] - prices[i - 1]) / prices[i - 1] * 100
        returns.append(daily_return)
    return returns


def calculate_moving_average(prices, window):
    if len(prices) < window:
        return None
    recent_prices = prices[-window:]
    average = sum(recent_prices) / len(recent_prices)
    return average


gold = yf.Ticker("GC=F")
data = gold.history(period="1mo")
close_prices = data["Close"].tolist()

print(f"Number of trading days: {len(close_prices)}")
print(f"Most recent gold price: ${close_prices[-1]:.2f}")
print(f"Oldest gold price: ${close_prices[0]:.2f}")

daily_returns = calculate_daily_returns(close_prices)
avg_return = sum(daily_returns) / len(daily_returns)
sma_5 = calculate_moving_average(close_prices, 5)
sma_20 = calculate_moving_average(close_prices, 20)

print(f"Average daily return: {avg_return:.2f}%")
print(f"5-day moving average: ${sma_5:.2f}")
print(f"20-day moving average: ${sma_20:.2f}")

Your script now crunches real gold price data into investment metrics. Next up, you'll use these metrics to generate actual buy, hold, or sell signals.

Generate Buy/Hold/Sell Signals

You now have real gold price data and calculated metrics like daily returns and moving averages. The next step is turning those numbers into an actionable recommendation.

In this step, you will write a function that uses a moving average crossover strategy to generate buy, hold, or sell signals. Then you will print everything as a clean, formatted investment report.

In this step, get ready to:
  • Write signal logic that compares the 5-day and 20-day moving averages.
  • Print a formatted Investment Analysis Report with all your metrics.
  • Run the script and see your buy/hold/sell recommendation for gold.
Write the signal logic

A moving average crossover is a trading strategy that compares a short-term average to a long-term average. When the short-term average is above the long-term average and the price is rising, it suggests bullish momentum. When it is below, it suggests bearish momentum.

  • In stock_analyzer.py, find the line gold = yf.Ticker("GC=F").
  • Add the following function above that line:
def generate_signal(current_price, sma_5, sma_20):
    # Check if we have enough data for both averages
    if sma_5 is None or sma_20 is None:
        return "NOT ENOUGH DATA"
    # Short-term above long-term and price rising = bullish
    if sma_5 > sma_20 and current_price > sma_5:
        return "BUY SIGNAL"
    # Short-term below long-term and price falling = bearish
    elif sma_5 < sma_20 and current_price < sma_5:
        return "SELL SIGNAL"
    else:
        return "HOLD"

What does this code do?

  • if sma_5 is None or sma_20 is None checks whether both averages exist. If there is not enough price data, the function returns early.
  • The and keyword combines two conditions on the same line. Both must be true for the signal to trigger.
  • elif stands for "else if". It lets you chain multiple conditions without nesting.
  • The function returns a plain string. Python functions can return any data type.

What is a moving average crossover?

A moving average crossover compares two averages of different time periods. The 5-day average reacts quickly to recent price changes. The 20-day average reflects the longer trend.

When the fast average crosses above the slow average, it signals that recent prices are outpacing the longer trend. Real traders use this exact concept (with more filters and risk management) to make buying and selling decisions.

You won't see a change yet. The next chunk uses this function to produce your final report.

Print a formatted analysis report

Now you will call generate_signal() and print everything as a clean report the way a real terminal-based tool would display results.

  • At the bottom of stock_analyzer.py, below the existing print statements, add this code:
# Get the latest price and generate the trading signal
current_price = close_prices[-1]
signal = generate_signal(current_price, sma_5, sma_20)

# Print a formatted analysis report
print("\n" + "=" * 50)
print("       INVESTMENT ANALYSIS REPORT")
print("=" * 50)
print(f"  Asset: Gold Futures (GC=F)")
print(f"  Current Price: ${current_price:.2f}/oz")
print(f"  5-Day SMA: ${sma_5:.2f}")
print(f"  20-Day SMA: ${sma_20:.2f}")
print(f"  Avg Daily Return: {avg_return:.2f}%")
print(f"  Signal: {signal}")
print("=" * 50)

What does this code do?

  • current_price = close_prices[-1] grabs the most recent closing price from your list. The -1 index always points to the last item.
  • generate_signal() is called with the three values it needs. It returns one of four strings.
  • "=" * 50 creates a line of 50 equals signs. This is string multiplication in Python.
  • "\n" adds an empty line before the report so it is visually separated from the earlier output.
  • Save stock_analyzer.py.
Run your script and see the report
  • Switch back to CMD and run your script by entering this command:
python stock_analyzer.py

You should see the individual metrics from earlier, followed by a bordered report showing the asset name, current price, both moving averages, average daily return, and a signal (BUY SIGNAL, SELL SIGNAL, or HOLD).

Not seeing the report?

  • If you see NameError: name 'generate_signal' is not defined, make sure the function is placed above the line gold = yf.Ticker("GC=F"). Python reads top to bottom, so the function must be defined before it is called.
  • If you see a SyntaxError, check that the elif keyword is spelled correctly and that each return line is indented with four spaces inside the function.
  • If the signal shows NOT ENOUGH DATA, the market may have fewer than 20 trading days in the last month (e.g. around holidays). This is expected and means the script is working correctly.

Still stuck? Ask for help debugging your script.

✔️ Awesome, I've got everything!

Great. Double check you have saved your file, then move on to the validation below.

ⓧ I'd like to double check the full code

Here is the complete stock_analyzer.py file at this point:

import yfinance as yf


def calculate_daily_returns(prices):
    returns = []
    for i in range(1, len(prices)):
        daily_return = (prices[i] - prices[i - 1]) / prices[i - 1] * 100
        returns.append(daily_return)
    return returns


def calculate_moving_average(prices, window):
    if len(prices) < window:
        return None
    recent_prices = prices[-window:]
    average = sum(recent_prices) / len(recent_prices)
    return average


def generate_signal(current_price, sma_5, sma_20):
    if sma_5 is None or sma_20 is None:
        return "NOT ENOUGH DATA"
    if sma_5 > sma_20 and current_price > sma_5:
        return "BUY SIGNAL"
    elif sma_5 < sma_20 and current_price < sma_5:
        return "SELL SIGNAL"
    else:
        return "HOLD"


gold = yf.Ticker("GC=F")
data = gold.history(period="1mo")
close_prices = data["Close"].tolist()

print(f"Number of trading days: {len(close_prices)}")
print(f"Most recent gold price: ${close_prices[-1]:.2f}")
print(f"Oldest gold price: ${close_prices[0]:.2f}")

daily_returns = calculate_daily_returns(close_prices)
avg_return = sum(daily_returns) / len(daily_returns)
sma_5 = calculate_moving_average(close_prices, 5)
sma_20 = calculate_moving_average(close_prices, 20)

print(f"Average daily return: {avg_return:.2f}%")
print(f"5-day moving average: ${sma_5:.2f}")
print(f"20-day moving average: ${sma_20:.2f}")

current_price = close_prices[-1]
signal = generate_signal(current_price, sma_5, sma_20)

print("\n" + "=" * 50)
print("       INVESTMENT ANALYSIS REPORT")
print("=" * 50)
print(f"  Asset: Gold Futures (GC=F)")
print(f"  Current Price: ${current_price:.2f}/oz")
print(f"  5-Day SMA: ${sma_5:.2f}")
print(f"  20-Day SMA: ${sma_20:.2f}")
print(f"  Avg Daily Return: {avg_return:.2f}%")
print(f"  Signal: {signal}")
print("=" * 50)

Secret mission

Add Multi-Asset Comparison

Your analyzer only looks at gold right now. What if you could compare gold, silver, and a stock side by side, then rank them by performance? That is what a real portfolio tool does.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.

Resources you used:

  • The stock-analyzer folder on your Desktop containing stock_analyzer.py.
  • The yfinance Python library (installed with pip).

Keep everything running

No action needed. Choose this if you plan to keep experimenting with your stock analyzer or want to add more features.

  • Your stock-analyzer folder stays on your Desktop.
  • The yfinance library remains installed and available for any Python script.

Pause - I'll come back to this later

Close your open tools to free up memory. Your files and installed libraries stay intact for when you return.

  • Close the CMD window.
  • Close Cursor.

Delete - I don't want to use this again

Remove all project resources and start fresh. This deletes your code and uninstalls the library.

  • Delete the stock-analyzer folder by running this command in CMD:
cd %USERPROFILE%\Desktop
rmdir /s /q stock-analyzer

CMD will ask you to confirm. Type Y and press Enter.

  • Uninstall the yfinance library by running:
pip uninstall yfinance -y

What about yfinance's dependencies?

When you installed yfinance, it also installed libraries like pandas and numpy. Uninstalling yfinance alone leaves those behind.

This is fine. They take minimal disk space and other Python projects may use them. If you want a fully clean slate, you can uninstall them individually with pip uninstall pandas numpy requests.

Nice Work!

Nice Work!

You just built a real investment analyzer from scratch using Python. From an empty file to a script that pulls live market data, calculates moving averages, and tells you whether to buy, hold, or sell.

Here's what you learned:

  • Written a Python script from scratch that fetches real gold price data using yfinance and pip.
  • Used Python fundamentals hands-on: variables, functions, loops, conditionals, lists, and f-string formatting.
  • Implemented a real trading strategy (moving average crossover) to generate buy/hold/sell signals from live market data.
  • Secret Mission: Built a multi-asset comparison tool that analyzes gold, silver, and Microsoft side by side and ranks them by performance.

Ready to quiz yourself?