Week 1: Introduction to Python and Financial Data Analysis

Version 5.0 - August 2026

Author
Affiliation

Jon Danielsson

London School of Economics

1 Week 1

Python is one of the four standard languages of quantitative finance, alongside R, Julia and MATLAB, and is the most widely used. Financial institutions use Python for portfolio optimisation, risk measurement, regulatory reporting and algorithmic trading. This course uses it on a smaller scale to obtain market data, manipulate it and draw conclusions about risk.

Python on its own does little of this. The work is done by a handful of libraries built on top of it, introduced below and listed in full in the course setup guide.

For more on Python and its use in risk forecasting, see the Python risk forecasting notebook.

NoteBefore you start

Complete the setup guide, open your main course folder in Positron, select Anaconda’s Python, and have your EODHD token available. Week 1 creates no file needed by later seminars. The permanent course data files are created in Week 2.

TipBy the end of this session

You should be able to download one asset, inspect its data, calculate log returns, explain the missing first return, and produce labelled price and return plots.

CautionUsing AI

AI use is permitted and encouraged in this course. Python is also the language of most artificial intelligence work and the one AI assistants write best, and the editor used in this course has one built in. They will write plausible code for anything in this course, some of which will be wrong in ways only a reader who understands the material will notice. Keep the last working version, ask for one limited change at a time, read the proposed code, rerun the analysis, check the result against the financial meaning and the checkpoints below, and be able to explain the resulting code and output. Never give an AI an API token.

1.1 The plan for this week

  1. Familiarisation with Python and the course environment
  2. Read the small set of Python notation used in the seminars
  3. Set up EOD Historical Data access
  4. Download and inspect one asset
  5. Calculate returns
  6. Create labelled price and return plots

1.2 The software

The course uses two applications. Anaconda supplies Python and most of the libraries. Positron is where you write and run code.

On LSE computers both are installed and configured, so there is nothing to do. On your own computer they have to be installed, Positron has to be pointed at Anaconda’s Python rather than at any other Python on the machine, and two further libraries have to be added. The setup guide covers all of it:

Work through it before the first seminar rather than during it.

1.3 The libraries

A Python library is a collection of reusable code written for a particular purpose. For example, pandas provides tools for working with data tables, while matplotlib provides plotting tools. A library is made available with import, at the top of a document or a session rather than part-way down, so that a reader can see at a glance what the code depends on. numpy, pandas and matplotlib are conventionally given short names, and those abbreviations are used everywhere in this course and in almost all Python code you will meet elsewhere. Each week imports the libraries it uses and nothing else. This one uses four:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from eodhd import APIClient

1.4 Basic Python commands

1 + 1
2 ** 5
a = 2
b = 100
np.exp(a) + b / 10
result = np.exp(a) + b
print("the answer is:", np.log(a), "or", result)
the answer is: 0.6931471805599453 or 107.38905609893065

1.5 Reading the Python used in this course

Notation Meaning
a = 2 = gives a value a name
print(a) parentheses call a function
prices["date"] square brackets select data
"date" quotation marks enclose text
prices.head() a dot accesses a method or attribute belonging to an object
# explanation # begins a comment that Python does not execute
indented lines indentation groups code inside a loop, function or conditional block

A displayed code block can be placed in a Python file, a notebook cell or a QMD code chunk. A notebook or QMD normally displays the value of the last expression in a block automatically. In a .py file, use print() for a value you want to see. Figures appear when plt.show() runs.

Keep the data files created in Week 2 in the main course folder opened in Positron. The seminars use relative filenames such as "Returns.csv", not paths naming a particular account or computer.

1.6 How to work through a seminar

Work in a file rather than typing into the Console alone. Anything typed into the Console is gone when the session ends, and a seminar is worth keeping.

  1. Use File > New File, choose Python File, and save it as week1.py in the main course folder.
  2. Type or paste the code from this seminar into it as you go. Typing it is slower and teaches more.
  3. Run the current line, or a block you have selected, with Cmd-Enter on macOS or Ctrl-Enter on Windows. Positron sends it to the Console and leaves the file as it is.
  4. Read the text output in the Console and look for figures in the Plots pane.
  5. If the state of the session becomes unclear, restart it and run the file again from the top. That is also the quickest way to find out whether the file works on its own.

Weeks 2 and 3 work the same way. Week 4 introduces QMD, which keeps the code and the report it produces in one document.

1.7 Setting up EOD Historical Data access

1.7.1 API token setup

An API token is like a digital key that identifies you to the EODHD service. The token is a unique string of characters that tells EODHD who you are and what data you are allowed to access.

If you are in my courses, you will get access to EODHD. For others, you can register at eodhistoricaldata.com for a free or paid account.

1.8 Downloading financial data

1.8.1 Setup

Put the token in your code and hand it to the client:

token = "demo"
api = APIClient(token)
WarningProtect your token

Treat a personal token like a password. Never paste it into an AI conversation, screenshot or shared file. If you use a QMD report, hide the token block from the rendered report with #| echo: false, and replace the real value with "YOUR_TOKEN" before sharing the source. Revoke and replace a token that is exposed.

1.8.2 What works with demo vs personal tokens

Demo tokens can access basic price data for a small set of symbols, including AAPL.US, TSLA.US, VTI.US, AMZN.US, BTC-USD.CC and EURUSD.FOREX, but not the full exchange coverage a personal token provides.

A personal token can access all functions without that restriction, though tiers of access still apply above the base plan.

With EODHD, we can download data directly in Python using ticker symbols in the form TICKER.EXCHANGE, for example AAPL.US. The get_eod_historical_stock_market_data() method of APIClient takes the symbol and the sampling period. Start with a single stock, Apple:

# Download Apple stock data
raw = api.get_eod_historical_stock_market_data(symbol="AAPL.US", period="d", order="a")
prices = pd.DataFrame(raw)
prices["date"] = pd.to_datetime(prices["date"])
  • The first line asks EODHD for daily Apple prices in ascending date order and stores the returned records in raw.
  • The second line converts those records into a pandas DataFrame called prices.
  • The third line converts the date column from text into pandas dates, so that Python can sort, select and plot observations by date.

1.8.3 Understanding the data structure

An EOD price download includes:

  • date: trading date
  • open, high, low, close: daily price range and closing price
  • adjusted_close: close price adjusted for splits and dividends
  • volume: number of shares traded

The eodhd client returns prices only, with no returns column, so we compute returns ourselves below.

print("Shape:", prices.shape)

print("\nFirst five rows:")
print(prices.head())

print("\nColumns:", prices.columns.tolist())

print("\nAdjusted-close summary:")
print(prices["adjusted_close"].describe())
Shape: (6470, 2)

First five rows:
        date  adjusted_close
0 2000-01-03      0.84010000
1 2000-01-04      0.76930000
2 2000-01-05      0.78050000
3 2000-01-06      0.71300000
4 2000-01-07      0.74680000

Columns: ['date', 'adjusted_close']

Adjusted-close summary:
count   6470.00000000
mean      46.10645808
std       66.05807253
min        0.19690000
25%        2.09690000
50%       15.41770000
75%       49.04847500
max      258.10380000
Name: adjusted_close, dtype: float64
NoteCheck your result

prices should have one row per trading day. Its columns should include date and adjusted_close, the dates should run forwards, and adjusted prices should be positive.

1.8.4 Computing returns

y = np.log(prices["adjusted_close"]).diff()
y.head()
0           NaN
1   -0.08803992
2    0.01445373
3   -0.09045332
4    0.04631599
Name: adjusted_close, dtype: float64

np.log() takes the natural logarithm of every adjusted price. .diff() then subtracts each day’s log price from the next one, so

\[ y_t = \log(P_t) - \log(P_{t-1}) = \log\left(\frac{P_t}{P_{t-1}}\right). \]

The resulting Series, stored as y, contains the daily continuously compounded returns.

diff() on a pandas Series is aligned by construction. It returns a Series of the same length as its input, with the first entry set to NaN rather than a number. The first return is undefined because it would need a price from the day before the sample starts, which we do not have. Keeping the length unchanged means the returns still line up with their dates, so nothing has to be padded back in by hand.

We can therefore add y directly as a column:

prices["return"] = y
prices[["date", "adjusted_close", "return"]].head()
date adjusted_close return
0 2000-01-03 0.84010000 NaN
1 2000-01-04 0.76930000 -0.08803992
2 2000-01-05 0.78050000 0.01445373
3 2000-01-06 0.71300000 -0.09045332
4 2000-01-07 0.74680000 0.04631599
NoteCheck your result

The first return should be NaN, and later returns should be decimal numbers, so a one per cent move is approximately 0.01 or -0.01, not 1 or -1.

describe() ignores missing values, so no trimmed copy of the DataFrame is needed:

prices["return"].describe()
count   6469.00000000
mean       0.00088318
std        0.02518992
min       -0.73134737
25%       -0.00984434
50%        0.00094095
75%        0.01250754
max        0.14261758
Name: return, dtype: float64

1.9 Visualising stock data

Plots come from matplotlib, which needs no extra styling to produce something usable.

fig, ax = plt.subplots()
ax.plot(prices["adjusted_close"])
plt.show()

This plot needs improvement. To begin with, we want dates on the x-axis rather than a plain row count.

fig, ax = plt.subplots()
ax.plot(prices["date"], prices["adjusted_close"])
plt.show()

We can do better:

fig, ax = plt.subplots()
ax.plot(prices["date"], prices["adjusted_close"])
ax.set_title("Apple stock prices")
ax.set_xlabel("Date")
ax.set_ylabel("Adjusted price")
plt.show()

Regular closing prices can show sudden jumps and drops caused by stock splits and dividend payments rather than company performance. For example, Apple had a 4-for-1 stock split on 31 August 2020, when the unadjusted price fell from around $500 to $125 without destroying three quarters of the company’s value. Adjusted prices correct for these corporate actions, which is why the risk calculations throughout this course use the adjusted series.

Or perhaps with a log y-axis, which is often more informative for a price series spanning decades:

fig, ax = plt.subplots()
ax.plot(prices["date"], prices["adjusted_close"])
ax.set_title("Apple stock prices")
ax.set_xlabel("Date")
ax.set_ylabel("Adjusted price")
ax.set_yscale("log")
plt.show()

The return series can be plotted in the same way:

fig, ax = plt.subplots()
ax.plot(prices["date"], prices["return"], color="darkred")
ax.set_title("Apple daily log returns")
ax.set_xlabel("Date")
ax.set_ylabel("Log return")
plt.show()

EODHD also provides dividend and split information through api.get_dividends(symbol=...) and api.get_splits(symbol=...), the latter requiring a personal token. These are left for the final exercises because they add another data operation without changing this week’s central analysis.

1.10 Recap

1.10.1 In this seminar we have covered:

  • Setting up the course Python environment and the eodhd client
  • Understanding API tokens and how to use them securely
    • Understanding demo token limitations
  • Downloading and exploring stock price data
  • Understanding the difference between regular and adjusted prices
  • Calculating log returns from price data with the aligned pandas method diff()
  • Creating progressively better visualisations:
    • Basic plots
    • Adding dates to axes
    • Improving labels and formatting
    • Using a log scale for better long-term visualisation

1.10.2 Key skills learned:

  • Working with pandas Series and DataFrames
  • Using np.log() and .diff() for return calculations
  • Understanding why the first return in a sample is undefined
  • Customising matplotlib plots with titles, labels and axis scales
  • Understanding why adjusted prices matter for analysis

1.10.3 Some new functions used:

  • APIClient() — creates an authenticated EODHD client
  • api.get_eod_historical_stock_market_data() — downloads historical price data for a symbol
  • pd.DataFrame() — builds a data frame from raw records
  • pd.read_csv() — reads a CSV file into a data frame
  • .shape, .head(), .columns — inspect a data frame’s dimensions, first rows and column names
  • .describe() — summary statistics for a Series or DataFrame
  • np.log() — computes the natural logarithm
  • .diff() — calculates differences between consecutive elements, aligned
  • plt.subplots(), ax.plot() — create and populate a figure

1.11 Exercises

1.11.1 Apply the method

Choose another stock available through your EODHD account. Change the symbol, download it, calculate its returns and update both plot titles. Before running the return calculation, predict what the first return will contain. Explain in one or two sentences why the price and return plots look so different.

1.11.2 Compare results

Compare Apple with one other stock. Use the same calculations for both, then compare their mean return and standard deviation. A larger return standard deviation means greater historical volatility. Check that this conclusion is consistent with the return plots.

1.11.3 Extend the analysis

  1. Download three stocks, plot their adjusted prices with a legend and create a table comparing their return volatility.
  2. Use api.get_dividends() for Apple, Microsoft and Exxon (XOM.US). Compare payment frequency and plot the dividend amounts over time.