| Time | Block |
|---|---|
| 0:00 – 0:23 | 1.1 Setting up Codespaces and running your first script |
| 0:23 – 0:53 | 1.2 Variables and arithmetic (real rates, compounding, annualising) |
| 0:53 – 1:00 | Break |
| 1:00 – 1:23 | 1.3 Data types · 1.4 Strings and f-strings |
| 1:23 – 1:53 | 1.5 Lists (time series) · 1.6 Dictionaries (indicator snapshots) |
| 1:53 – 2:00 | Break |
| 2:00 – 2:23 | 1.7 Day 1 mini-project: Macro Snapshot Printer |
| 2:23 – 2:30 | 1.8 Save your work with Git |
1.1 Setting up Codespaces (23 min)
Step 1 — Create a repository. On GitHub, click New repository, name it macro-data-course, tick Add a README, and click Create repository.
Step 2 — Launch a Codespace. On the repository page click Code → Codespaces → Create codespace on main. After a minute you'll see VS Code in your browser.
Step 3 — Find the terminal. At the bottom of the screen there is a Terminal panel (if not, press Ctrl+`). The terminal is where you type commands to the computer.
python --version
You should see something like Python 3.12.x. Python is already installed in Codespaces.
Step 4 — Try Python interactively. Type python and press Enter. You're now in the Python interpreter (the prompt changes to >>>). Try:
>>> 2 + 2
4
>>> 2.25 - 3.0
-0.75
>>> exit()
The interpreter is great for quick checks, but real work lives in scripts (.py files) that we can save, rerun, and share.
Step 5 — Your first script.
mkdir day01
In the Explorer panel (left), right-click day01 → New File → hello.py:
# hello.py — my first Python script
# Lines starting with # are comments. Python ignores them.
print("Hello, macroeconomics!")
print("The Bank of Canada targets 2% inflation.")
Run it from the terminal:
python day01/hello.py
Key idea:
print()displays output. Comments (#) explain why — write them generously.
1.2 Variables and Arithmetic (30 min)
A variable is a labelled box holding a value. Economists already think this way: i is the nominal rate, π is inflation.
Example 1 — The real interest rate (approximation)
Create day01/rates.py:
# Values from the course notebook (latest observations)
overnight_rate = 2.25 # BoC policy rate, %
cpi_inflation = 3.0 # CPI all-items, year-over-year %
real_rate = overnight_rate - cpi_inflation
print("Real interest rate (approx.):")
print(real_rate)
Output: -0.75. A negative real rate means policy is stimulative in real terms.
Naming rules: use lowercase with underscores (
cpi_inflation, notCPI Inflation). Names can't start with a number or contain spaces. Pick names a colleague would understand.
Example 2 — The exact Fisher equation
The approximation r ≈ i − π works for small rates. The exact relation is (1 + r) = (1 + i) / (1 + π). Python uses ** for powers and parentheses as in algebra. Add to rates.py:
# Convert percentages to decimals
i = overnight_rate / 100
pi = cpi_inflation / 100
real_rate_exact = (1 + i) / (1 + pi) - 1
print("Real interest rate (exact Fisher), %:")
print(real_rate_exact * 100)
Output: about -0.728. The two measures differ slightly — worth remembering when inflation is high.
Example 3 — Compound growth
Federal GDP in the notebook's fiscal table for 2026 is about $3,180B. What if nominal GDP grows 1.5% a year for 5 years?
gdp_2026 = 3180 # $ billions
growth = 0.015 # 1.5% per year
years = 5
gdp_future = gdp_2026 * (1 + growth) ** years
print("Projected GDP in 5 years ($B):")
print(gdp_future)
Output: about 3425.8.
Example 4 — Annualising quarterly growth
Statistics Canada reports quarterly GDP growth, but headlines quote annualised rates. The notebook uses the shortcut qoq × 4. The compounded version is (1 + qoq)^4 − 1.
qoq_growth = 0.008 # 0.8% quarter-over-quarter
annualised_simple = qoq_growth * 4
annualised_compound = (1 + qoq_growth) ** 4 - 1
print("Simple annualised:", annualised_simple * 100)
print("Compound annualised:", annualised_compound * 100)
Output: 3.2 vs about 3.24. Notice print can take several items separated by commas.
Python's arithmetic operators
| Operator | Meaning | Example | Result |
|---|---|---|---|
+ - | add, subtract | 5.0 - 2.25 | 2.75 |
* / | multiply, divide | 100 / 4 | 25.0 |
** | power | 1.02 ** 10 | 1.2189... |
// | whole-number division | 14 // 4 | 3 |
% | remainder | 14 % 4 | 2 |
Try it 1.2
A 10-year Government of Canada bond yields 3.75% and the 2-year yields 2.27% (notebook values). (a) Compute the 10yr−2yr spread in percentage points. (b) Convert it to basis points (1 pp = 100 bp). (c) If $1,000 is invested at 3.75% annually for 10 years, what is it worth?
Solution
yield_10yr = 3.75
yield_2yr = 2.27
spread_pp = yield_10yr - yield_2yr
spread_bp = spread_pp * 100
investment = 1000 * (1 + yield_10yr / 100) ** 10
print("Spread (pp):", spread_pp)
print("Spread (bp):", spread_bp)
print("Value after 10 years:", investment)
Output: spread ≈ 1.48 pp ≈ 148 bp; investment ≈ $1,444.5. You may see 1.4800000000000002 — we explain why in the next block.
1.3 Data Types (13 min)
Every value has a type. The four you need today:
| Type | What it holds | Examples |
|---|---|---|
int | whole numbers | 2026, 12, -3 |
float | decimals | 2.25, -0.75 |
str | text | "Canada", "2026-08-01" |
bool | true/false | True, False |
Example 1 — Checking types
print(type(2026)) # <class 'int'>
print(type(2.25)) # <class 'float'>
print(type("V39079")) # <class 'str'>
print(type(True)) # <class 'bool'>
Example 2 — Floating-point surprises
print(0.1 + 0.2) # 0.30000000000000004
print(round(0.1 + 0.2, 2)) # 0.3
Computers store decimals in binary, so tiny errors appear. This is harmless for economics, but round when displaying and never test decimals with exact equality.
Example 3 — Converting types (very important for APIs)
The Bank of Canada API returns numbers as text. Look at this line in the notebook's boc_fetch: float(v). That's a type conversion.
rate_from_api = "2.25" # this is a string!
print(rate_from_api + "1") # "2.251" — text gets glued together
rate = float(rate_from_api) # convert to a number
print(rate + 1) # 3.25
year = int("2026")
print(year + 1) # 2027
label = str(2.25)
print("Rate: " + label + "%") # Rate: 2.25%
Common error:
float("NA")orfloat("Bank holiday")crashes with aValueError. We handle this on Day 2.
1.4 Strings and f-strings (10 min)
f-strings let you insert variables into text and control formatting. Put f before the quotes and variables in {}.
Example 1 — Basic f-string
country = "Canada"
unemployment = 6.4
print(f"The unemployment rate in {country} is {unemployment}%.")
Example 2 — Formatting numbers
real_rate = -0.7281553398
housing_starts = 66788
spread_pp = 1.4800000000000002
print(f"Real rate: {real_rate:.2f}%") # 2 decimals → -0.73%
print(f"Real rate: {real_rate:+.2f}%") # force sign → -0.73%
print(f"Housing starts: {housing_starts:,}") # thousands separator → 66,788
print(f"Spread: {spread_pp * 100:.0f} bp") # no decimals → 148 bp
print(f"Growth: {0.0324:.1%}") # as percentage → 3.2%
Example 3 — Useful string methods
series_name = " Unemployment Rate "
print(series_name.strip()) # removes outer spaces
print(series_name.strip().lower()) # "unemployment rate"
print("2026-08-01"[:4]) # first 4 characters → "2026"
print("V39079" == "v39079") # False — text comparison is case-sensitive
The notebook uses
df.columns.str.strip()for exactly this reason — StatCan headers sometimes contain stray spaces.
1.5 Lists: Your First Time Series (18 min)
A list is an ordered collection — a natural container for a time series.
Example 1 — Creating and inspecting a list
# Illustrative monthly CPI index levels, Jan–Dec
cpi = [161.3, 161.8, 162.4, 162.6, 163.0, 163.4,
163.9, 164.0, 163.7, 164.1, 164.3, 164.2]
print(cpi)
print("Number of months:", len(cpi))
print("First value:", cpi[0]) # Python counts from 0!
print("Last value:", cpi[-1]) # negative index counts from the end
Zero-based indexing: January is
cpi[0], December iscpi[11](orcpi[-1]). This trips everyone up at first.
Example 2 — Slicing (sub-periods)
q1 = cpi[0:3] # items 0, 1, 2 (stop index is excluded)
h2 = cpi[6:] # from July to the end
last_3 = cpi[-3:] # last three months
print("Q1:", q1)
print("H2:", h2)
print("Last 3 months:", last_3)
Example 3 — Summary statistics with built-ins
average = sum(cpi) / len(cpi)
print(f"Average CPI level: {average:.2f}")
print(f"Min: {min(cpi)}, Max: {max(cpi)}")
# Change over the year (Dec vs Jan)
change = (cpi[-1] / cpi[0] - 1) * 100
print(f"Jan→Dec change: {change:.2f}%")
Example 4 — Modifying lists
cpi.append(164.9) # add next January's value
print(len(cpi)) # 13
dates = ["2026-01", "2026-02", "2026-03"]
dates.append("2026-04")
print(dates)
Try it 1.5
Using the notebook's BoC decision rates for 2024 (in order): [4.75, 4.50, 4.25, 3.75, 3.25]
(a) Print the first and last rates. (b) Compute the total change in basis points. (c) Print the number of decisions.
Solution
rates_2024 = [4.75, 4.50, 4.25, 3.75, 3.25]
print("First:", rates_2024[0], "Last:", rates_2024[-1])
total_change_bp = (rates_2024[-1] - rates_2024[0]) * 100
print(f"Total change: {total_change_bp:.0f} bp")
print("Decisions:", len(rates_2024))
Output: -150 bp, 5 decisions.
1.6 Dictionaries: Indicator Snapshots (13 min)
A dictionary maps keys to values. Think of it as a labelled row: indicator name → latest value. The notebook's BOC_SERIES and TARIFF_STATUS are dictionaries.
Example 1 — Building a snapshot
snapshot = {
"overnight_rate": 2.25,
"cpi_yoy": 3.0,
"unemployment": 6.4,
"usd_cad": 1.3864,
}
print(snapshot["unemployment"]) # look up by key → 6.4
Example 2 — Adding and updating
snapshot["real_rate"] = snapshot["overnight_rate"] - snapshot["cpi_yoy"]
snapshot["unemployment"] = 6.5 # a revised value replaces the old one
print(snapshot)
print(list(snapshot.keys()))
Example 3 — Series codes (a pattern you'll see all course)
BOC_SERIES = {
"V39079": "overnight_rate",
"V122531": "yield_2yr",
"BD.CDN.10YR.DQ.YLD": "yield_10yr",
}
code = "V39079"
print(f"Series {code} is the {BOC_SERIES[code]}")
print("V122538" in BOC_SERIES) # False — check before looking up
Common error:
snapshot["gdp"]raises aKeyErrorif the key doesn't exist. Usesnapshot.get("gdp"), which returnsNoneinstead of crashing.
1.7 Day 1 Mini-Project: Macro Snapshot Printer (23 min)
Task: Create day01/snapshot.py that stores the latest indicators in a dictionary, derives the real rate and 10yr−2yr spread, and prints a neatly formatted briefing.
Expected output (roughly):
=== Canadian Macro Snapshot ===
Overnight rate: 2.25%
CPI inflation: 3.0%
Real rate: -0.75%
Unemployment: 6.4%
10yr-2yr spread: 148 bp
USD/CAD: 1.3864
Housing starts: 66,788
Solution
# day01/snapshot.py — prints a formatted macro briefing
snapshot = {
"overnight_rate": 2.25,
"cpi_yoy": 3.0,
"unemployment": 6.4,
"yield_10yr": 3.75,
"yield_2yr": 2.27,
"cad_usd": 0.7213,
"housing_starts": 66788,
}
# Derived indicators
snapshot["real_rate"] = snapshot["overnight_rate"] - snapshot["cpi_yoy"]
snapshot["spread_bp"] = (snapshot["yield_10yr"] - snapshot["yield_2yr"]) * 100
snapshot["usd_cad"] = 1 / snapshot["cad_usd"]
print("=== Canadian Macro Snapshot ===")
print(f"Overnight rate: {snapshot['overnight_rate']:>6.2f}%")
print(f"CPI inflation: {snapshot['cpi_yoy']:>6.1f}%")
print(f"Real rate: {snapshot['real_rate']:>6.2f}%")
print(f"Unemployment: {snapshot['unemployment']:>6.1f}%")
print(f"10yr-2yr spread: {snapshot['spread_bp']:>6.0f} bp")
print(f"USD/CAD: {snapshot['usd_cad']:>6.4f}")
print(f"Housing starts: {snapshot['housing_starts']:>6,}")
Note: :>6.2f means right-align in 6 characters with 2 decimals. Inside an f-string with double quotes, use single quotes for dictionary keys.
Stretch: add a line showing the exact Fisher real rate next to the approximation.
1.8 Save Your Work with Git (8 min)
Git records snapshots of your project. In the terminal:
git add .
git commit -m "Day 1: variables, lists, dictionaries, snapshot printer"
git push
Refresh your GitHub repository page — your files are there. Do this at the end of every day.
Day 1 checklist
- I can run a
.pyscript from the terminal - I can compute real rates, compound growth, and annualised growth
- I know the difference between
int,float,str,booland whyfloat("2.25")matters - I can format numbers with f-strings
- I can index and slice a list, and look up values in a dictionary