---
title: "Python Visual Exercises"
---
## Purpose
This appendix collects visual exercises used in NREC4410. Each exercise combines a short trade question, a small numerical setup, and a Python graph.
The purpose is not to teach advanced programming. The purpose is to help students see how trade models behave when prices, quantities, tariffs, and policy assumptions change.
::: {.callout-tip}
## How to use this appendix
Read the economic question first. Then run the code and interpret the figure. In exams and projects, the important part is the economic interpretation, not memorizing the code.
:::
## Exercise 1. Ricardian PPF and TPF
### Economic question
Oman and Kuwait both produce food and cloth using labor only. Oman is more efficient in food, while Kuwait is more efficient in cloth.
| Country | Labor needed for 1 food | Labor needed for 1 cloth | Labor supply |
|---|---:|---:|---:|
| Oman | 1 | 2 | 2000 |
| Kuwait | 2 | 1 | 2000 |
Tasks:
1. Draw the production possibility frontier for each country.
2. Identify comparative advantage.
3. Draw the trade possibility frontier when 1 unit of food trades for 1 unit of cloth.
### Interpretation before graphing
Oman can produce at most 2000 units of food or 1000 units of cloth. Kuwait can produce at most 1000 units of food or 2000 units of cloth.
Oman has comparative advantage in food. Kuwait has comparative advantage in cloth.
```{python}
#| label: fig-visual-ricardian-ppf-tpf
#| fig-cap: "Ricardian production and trade possibility frontiers for Oman and Kuwait."
#| fig-width: 9
#| fig-height: 4.5
#| echo: true
import numpy as np
import matplotlib.pyplot as plt
countries = {
"Oman": {"a_food": 1, "a_cloth": 2, "L": 2000, "specializes": "Food"},
"Kuwait": {"a_food": 2, "a_cloth": 1, "L": 2000, "specializes": "Cloth"}
}
world_price_food_in_cloth = 1
fig, axes = plt.subplots(1, 2, figsize=(9, 4.5))
for ax, (country, pars) in zip(axes, countries.items()):
a_f = pars["a_food"]
a_c = pars["a_cloth"]
L = pars["L"]
max_food = L / a_f
max_cloth = L / a_c
food = np.linspace(0, max_food, 200)
cloth_ppf = (L - a_f * food) / a_c
if pars["specializes"] == "Food":
food_tpf = np.linspace(0, max_food, 200)
cloth_tpf = (max_food - food_tpf) * world_price_food_in_cloth
else:
max_food_trade = max_cloth / world_price_food_in_cloth
food_tpf = np.linspace(0, max_food_trade, 200)
cloth_tpf = max_cloth - food_tpf * world_price_food_in_cloth
ax.plot(food, cloth_ppf, label="PPF")
ax.plot(food_tpf, cloth_tpf, linestyle="--", label="TPF after trade")
ax.fill_between(food, cloth_ppf, alpha=0.15)
ax.set_title(country)
ax.set_xlabel("Food")
ax.set_ylabel("Cloth")
ax.set_xlim(0, max(max_food, food_tpf.max()) * 1.05)
ax.set_ylim(0, max(max_cloth, cloth_tpf.max()) * 1.05)
ax.legend()
plt.tight_layout()
plt.show()
```
### Key lesson
Trade expands consumption possibilities. A country gains from specializing in the good where its opportunity cost is lower, not necessarily the good where its absolute productivity is highest.
## Exercise 2. Heckscher-Ohlin PPF with labor and capital constraints
### Economic question
A country produces automobiles and textiles. Both labor and capital are limited. The feasible production area is determined by the labor and capital constraints.
| Good | Labor requirement | Capital requirement |
|---|---:|---:|
| Automobile | 2 | 4 |
| Textile | 4 | 2 |
Available resources:
| Resource | Amount |
|---|---:|
| Labor | 4000 |
| Capital | 6000 |
Tasks:
1. Draw the labor constraint.
2. Draw the capital constraint.
3. Identify the production possibility frontier.
4. Interpret why the PPF is shaped by the more binding constraint.
```{python}
#| label: fig-visual-ho-ppf
#| fig-cap: "Heckscher-Ohlin production constraints and PPF."
#| fig-width: 7
#| fig-height: 5
#| echo: true
import numpy as np
import matplotlib.pyplot as plt
labor = 4000
capital = 6000
aL_auto, aL_textile = 2, 4
aK_auto, aK_textile = 4, 2
auto = np.linspace(0, 2000, 400)
textile_labor = (labor - aL_auto * auto) / aL_textile
textile_capital = (capital - aK_auto * auto) / aK_textile
textile_ppf = np.minimum(textile_labor, textile_capital)
textile_ppf = np.maximum(textile_ppf, 0)
fig, ax = plt.subplots(figsize=(7, 5))
ax.plot(auto, textile_labor, label="Labor constraint")
ax.plot(auto, textile_capital, label="Capital constraint")
ax.plot(auto, textile_ppf, linewidth=2.5, label="PPF")
ax.fill_between(auto, textile_ppf, alpha=0.15, label="Feasible area")
ax.set_xlabel("Automobiles")
ax.set_ylabel("Textiles")
ax.set_title("PPF with labor and capital constraints")
ax.set_xlim(0, 1700)
ax.set_ylim(0, 2100)
ax.legend()
plt.tight_layout()
plt.show()
```
### Key lesson
With more than one factor of production, production is constrained by the availability of each factor. A country cannot simply expand both goods if one factor becomes binding.
## Exercise 3. Factor prices after a goods-price change
### Economic question
Suppose the unit cost equations are:
$$
2w + 2r = P_C
$$
$$
w + 3r = P_F
$$
where $w$ is the wage, $r$ is the rental rate of capital, $P_C$ is the price of cloth, and $P_F$ is the price of food.
Initially:
$$
P_C = 4, \quad P_F = 4
$$
After trade, suppose the price of cloth rises:
$$
P_C = 6, \quad P_F = 4
$$
Tasks:
1. Solve for the initial wage and rental rate.
2. Solve for the new wage and rental rate.
3. Interpret who gains and who loses.
```{python}
#| label: fig-visual-factor-prices
#| fig-cap: "Factor-price equations before and after a rise in the price of cloth."
#| fig-width: 7
#| fig-height: 5
#| echo: true
import numpy as np
import matplotlib.pyplot as plt
w = np.linspace(0, 3.2, 300)
# Equations: 2w + 2r = Pc and w + 3r = Pf
r_cloth_initial = (4 - 2 * w) / 2
r_food = (4 - w) / 3
r_cloth_new = (6 - 2 * w) / 2
# Solved values
initial_w, initial_r = 1, 1
new_w, new_r = 2.5, 0.5
fig, ax = plt.subplots(figsize=(7, 5))
ax.plot(w, r_cloth_initial, label="Cloth price equation, Pc = 4")
ax.plot(w, r_food, label="Food price equation, Pf = 4")
ax.plot(w, r_cloth_new, linestyle="--", label="Cloth price equation, Pc = 6")
ax.scatter([initial_w], [initial_r], label="Initial factor prices")
ax.scatter([new_w], [new_r], label="New factor prices")
ax.axvline(initial_w, linestyle=":")
ax.axhline(initial_r, linestyle=":")
ax.axvline(new_w, linestyle=":")
ax.axhline(new_r, linestyle=":")
ax.set_xlabel("Wage, w")
ax.set_ylabel("Rental rate, r")
ax.set_title("Goods prices and factor prices")
ax.set_xlim(0, 3.2)
ax.set_ylim(0, 3.2)
ax.legend()
plt.tight_layout()
plt.show()
```
### Key lesson
When the price of the labor-intensive good rises, the wage can rise by more than the goods price, while the rental rate can fall. This is the basic Stolper-Samuelson logic.
## Exercise 4. Welfare before and after trade
### Economic question
There are two countries. Demand and supply are:
Country 1:
$$
Q_D = 80 - P, \quad Q_S = P
$$
Country 2:
$$
Q_D = 100 - 0.5P, \quad Q_S = 0.5P
$$
Tasks:
1. Compute autarky equilibrium in both countries.
2. Find the world price under free trade.
3. Compare consumer surplus, producer surplus, and total surplus before and after trade.
```{python}
#| label: fig-visual-welfare-before-after-trade
#| fig-cap: "Consumer and producer surplus before and after trade."
#| fig-width: 9
#| fig-height: 7
#| echo: true
import numpy as np
import matplotlib.pyplot as plt
# Country 1
P1_autarky = 40
Q1_autarky = 40
P_world = 60
Qd1_world = 20
Qs1_world = 60
# Country 2
P2_autarky = 100
Q2_autarky = 50
Qd2_world = 70
Qs2_world = 30
P1 = np.linspace(0, 80, 300)
Qd1 = 80 - P1
Qs1 = P1
P2 = np.linspace(0, 200, 300)
Qd2 = 100 - 0.5 * P2
Qs2 = 0.5 * P2
fig, axes = plt.subplots(2, 2, figsize=(9, 7))
# Country 1 autarky
ax = axes[0, 0]
ax.plot(Qd1, P1, label="Demand")
ax.plot(Qs1, P1, label="Supply")
ax.fill_betweenx(P1, 0, Qd1, where=(P1 >= P1_autarky), alpha=0.15, label="CS")
ax.fill_betweenx(P1, 0, Qs1, where=(P1 <= P1_autarky), alpha=0.15, label="PS")
ax.scatter([Q1_autarky], [P1_autarky])
ax.set_title("Country 1 before trade")
ax.set_xlabel("Quantity")
ax.set_ylabel("Price")
ax.set_xlim(0, 85)
ax.set_ylim(0, 85)
ax.legend()
# Country 1 after trade
ax = axes[0, 1]
ax.plot(Qd1, P1, label="Demand")
ax.plot(Qs1, P1, label="Supply")
ax.axhline(P_world, linestyle="--", label="World price")
ax.fill_betweenx(P1, 0, Qd1, where=(P1 >= P_world), alpha=0.15, label="CS")
ax.fill_betweenx(P1, 0, Qs1, where=(P1 <= P_world), alpha=0.15, label="PS")
ax.scatter([Qd1_world, Qs1_world], [P_world, P_world])
ax.text(40, 63, "Exports = 40", ha="center")
ax.set_title("Country 1 after trade")
ax.set_xlabel("Quantity")
ax.set_ylabel("Price")
ax.set_xlim(0, 85)
ax.set_ylim(0, 85)
ax.legend()
# Country 2 autarky
ax = axes[1, 0]
ax.plot(Qd2, P2, label="Demand")
ax.plot(Qs2, P2, label="Supply")
ax.fill_betweenx(P2, 0, Qd2, where=(P2 >= P2_autarky), alpha=0.15, label="CS")
ax.fill_betweenx(P2, 0, Qs2, where=(P2 <= P2_autarky), alpha=0.15, label="PS")
ax.scatter([Q2_autarky], [P2_autarky])
ax.set_title("Country 2 before trade")
ax.set_xlabel("Quantity")
ax.set_ylabel("Price")
ax.set_xlim(0, 105)
ax.set_ylim(0, 205)
ax.legend()
# Country 2 after trade
ax = axes[1, 1]
ax.plot(Qd2, P2, label="Demand")
ax.plot(Qs2, P2, label="Supply")
ax.axhline(P_world, linestyle="--", label="World price")
ax.fill_betweenx(P2, 0, Qd2, where=(P2 >= P_world), alpha=0.15, label="CS")
ax.fill_betweenx(P2, 0, Qs2, where=(P2 <= P_world), alpha=0.15, label="PS")
ax.scatter([Qs2_world, Qd2_world], [P_world, P_world])
ax.text(50, 65, "Imports = 40", ha="center")
ax.set_title("Country 2 after trade")
ax.set_xlabel("Quantity")
ax.set_ylabel("Price")
ax.set_xlim(0, 105)
ax.set_ylim(0, 205)
ax.legend()
plt.tight_layout()
plt.show()
```
### Key lesson
The exporting country’s producers gain and consumers lose. The importing country’s consumers gain and producers lose. In both countries, the gain to winners is larger than the loss to losers, so total surplus rises.
## Exercise 5. Supply shock in the exporting country
### Economic question
Start from the same two-country model. Now suppose Country 1’s supply becomes less favorable:
$$
Q_S = 0.5P
$$
Tasks:
1. Recompute the world price.
2. Compare the new trade volume with the original free-trade case.
3. Explain why the importer is also affected by the exporter’s supply shock.
```{python}
#| label: fig-visual-exporter-supply-shock
#| fig-cap: "Free trade after a negative supply shock in the exporting country."
#| fig-width: 9
#| fig-height: 4.5
#| echo: true
import numpy as np
import matplotlib.pyplot as plt
P_world_new = 72
Qd1_new = 8
Qs1_new = 36
Qd2_new = 64
Qs2_new = 36
P1 = np.linspace(0, 80, 300)
Qd1 = 80 - P1
Qs1_new_curve = 0.5 * P1
P2 = np.linspace(0, 200, 300)
Qd2 = 100 - 0.5 * P2
Qs2 = 0.5 * P2
fig, axes = plt.subplots(1, 2, figsize=(9, 4.5))
ax = axes[0]
ax.plot(Qd1, P1, label="Demand")
ax.plot(Qs1_new_curve, P1, label="New supply")
ax.axhline(P_world_new, linestyle="--", label="New world price")
ax.scatter([Qd1_new, Qs1_new], [P_world_new, P_world_new])
ax.text(22, 75, "Exports = 28", ha="center")
ax.set_title("Country 1 after supply shock")
ax.set_xlabel("Quantity")
ax.set_ylabel("Price")
ax.set_xlim(0, 85)
ax.set_ylim(0, 85)
ax.legend()
ax = axes[1]
ax.plot(Qd2, P2, label="Demand")
ax.plot(Qs2, P2, label="Supply")
ax.axhline(P_world_new, linestyle="--", label="New world price")
ax.scatter([Qs2_new, Qd2_new], [P_world_new, P_world_new])
ax.text(50, 77, "Imports = 28", ha="center")
ax.set_title("Country 2 after supply shock")
ax.set_xlabel("Quantity")
ax.set_ylabel("Price")
ax.set_xlim(0, 105)
ax.set_ylim(0, 205)
ax.legend()
plt.tight_layout()
plt.show()
```
### Key lesson
A supply shock in the exporting country raises the world price and reduces trade volume. Importing countries are affected even if their own demand and supply curves do not change.
## Exercise 6. Trade creation and trade diversion in an FTA
### Economic question
The UK imports all compact cars. Its import demand is:
$$
Q = 70 - 0.01P
$$
Quantities are in thousand cars and prices are in US dollars per car.
Before an FTA:
- Japan price = $5000
- Germany price = $5500
- UK tariff = $1000 on all imports
After an FTA with Germany, the tariff is removed only on German cars.
Tasks:
1. Show the delivered prices before and after the FTA.
2. Identify whether trade diversion occurs.
3. Calculate whether the FTA produces a welfare gain or welfare loss.
```{python}
#| label: fig-visual-fta-trade-diversion
#| fig-cap: "UK compact car import demand and delivered prices before and after an FTA."
#| fig-width: 7
#| fig-height: 5
#| echo: true
import numpy as np
import matplotlib.pyplot as plt
P = np.linspace(4500, 6500, 300)
Q = 70 - 0.01 * P
price_before = 6000
price_after = 5500
quantity_before = 70 - 0.01 * price_before
quantity_after = 70 - 0.01 * price_after
fig, ax = plt.subplots(figsize=(7, 5))
ax.plot(Q, P, label="Import demand")
ax.axhline(price_before, linestyle="--", label="Before FTA: Japan with tariff")
ax.axhline(price_after, linestyle="--", label="After FTA: Germany duty-free")
ax.axvline(quantity_before, linestyle=":")
ax.axvline(quantity_after, linestyle=":")
ax.scatter([quantity_before, quantity_after], [price_before, price_after])
ax.text(quantity_before + 0.5, price_before + 40, "Q0")
ax.text(quantity_after + 0.5, price_after + 40, "Q1")
ax.set_xlabel("Imports, thousand cars")
ax.set_ylabel("Price per car, USD")
ax.set_title("FTA effect in the UK compact car market")
ax.set_xlim(0, 25)
ax.set_ylim(4500, 6500)
ax.legend()
plt.tight_layout()
plt.show()
```
### Key lesson
An FTA can lower the price paid by consumers, but it can also divert imports from the world’s lower-cost supplier to a higher-cost partner. The welfare effect depends on whether the consumer-surplus gain is larger than the lost tariff revenue and trade-diversion cost.
## Exercise 7. Effective rate of protection
### Economic question
Cheese is worth $20 per kg at world prices. Producing 1 kg of cheese requires:
- 10 liters of milk at $1 per liter
- 10 grams of starting culture at $0.50 per gram
A country imposes:
- 20 percent tariff on cheese
- 10 percent tariff on milk
- 10 percent tariff on starting culture
Tasks:
1. Compute value added at world prices.
2. Compute value added at domestic tariff-inclusive prices.
3. Calculate the effective rate of protection.
```{python}
#| label: fig-visual-effective-protection
#| fig-cap: "World and domestic value added in the cheese example."
#| fig-width: 7
#| fig-height: 4.5
#| echo: true
import pandas as pd
import matplotlib.pyplot as plt
world_output = 20
world_inputs = 10 * 1 + 10 * 0.5
world_value_added = world_output - world_inputs
domestic_output = 20 * 1.20
domestic_inputs = 10 * 1 * 1.10 + 10 * 0.5 * 1.10
domestic_value_added = domestic_output - domestic_inputs
erp = (domestic_value_added - world_value_added) / world_value_added * 100
values = pd.DataFrame({
"Scenario": ["World prices", "Domestic prices after tariffs"],
"Output value": [world_output, domestic_output],
"Input cost": [world_inputs, domestic_inputs],
"Value added": [world_value_added, domestic_value_added]
})
print(values)
print(f"Effective rate of protection = {erp:.1f}%")
ax = values.set_index("Scenario")[["Output value", "Input cost", "Value added"]].plot(kind="bar")
ax.set_ylabel("US dollars per kg")
ax.set_title("Effective protection in cheese production")
plt.xticks(rotation=0)
plt.tight_layout()
plt.show()
```
### Key lesson
The nominal tariff on the final good does not fully describe the protection received by domestic producers. The effective rate of protection depends on tariffs on both outputs and inputs.
## Exercise 8. Oman-India CEPA simulation results
### Economic question
The Oman-India CEPA can be studied using product-level partial-equilibrium simulations. The TINA simulation separates total trade effects into trade creation and trade diversion.
Tasks:
1. Compare trade creation and trade diversion for Oman exports to India and Indian exports to Oman.
2. Explain why India’s gains are spread across more products.
3. Discuss why Oman’s gains are more concentrated.
```{python}
#| label: fig-visual-cepa-trade-effects
#| fig-cap: "Aggregate Oman-India CEPA simulation outcomes from TINA."
#| fig-width: 7
#| fig-height: 4.5
#| echo: true
import pandas as pd
import matplotlib.pyplot as plt
cepa = pd.DataFrame({
"Direction": ["Oman exports to India", "Indian exports to Oman"],
"Trade creation": [402.80, 550.61],
"Trade diversion": [164.67, 106.02]
})
ax = cepa.set_index("Direction").plot(kind="bar")
ax.set_ylabel("USD million")
ax.set_title("CEPA trade creation and trade diversion")
plt.xticks(rotation=0)
plt.tight_layout()
plt.show()
```
### Key lesson
Both partners gain trade, but the pattern is asymmetric. Oman’s gains are more concentrated in energy and chemical products, while India’s gains are broader and include food-related products.
## Summary checklist
After completing these visual exercises, students should be able to:
- draw and interpret PPF and TPF diagrams,
- identify comparative advantage,
- interpret factor-price diagrams,
- calculate CS, PS, and TS,
- explain exporter and importer welfare changes,
- interpret supply shocks in world markets,
- distinguish trade creation from trade diversion,
- calculate effective rate of protection,
- connect TINA simulation outputs to policy interpretation.