27. 商品价格#
27.1. 大纲#
商品的例子包括铜、钻石、铁矿石、锂、棉花和咖啡豆。
本讲将介绍商品价格理论。
相比本系列的其他讲座,这一讲内容较为高级。
我们需要计算一个由价格函数描述的均衡。
我们将解一个方程,其中价格函数是未知量。
这比解一个未知数或向量的方程要难得多。
本讲将讨论一种解函数方程的方法,这类方程的未知对象是函数。
本讲需要使用yfinance
库。
!pip install yfinance
Show output
Collecting yfinance
Downloading yfinance-0.2.55-py2.py3-none-any.whl.metadata (5.8 kB)
Requirement already satisfied: pandas>=1.3.0 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from yfinance) (2.2.2)
Requirement already satisfied: numpy>=1.16.5 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from yfinance) (1.26.4)
Requirement already satisfied: requests>=2.31 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from yfinance) (2.32.3)
Collecting multitasking>=0.0.7 (from yfinance)
Downloading multitasking-0.0.11-py3-none-any.whl.metadata (5.5 kB)
Requirement already satisfied: platformdirs>=2.0.0 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from yfinance) (3.10.0)
Requirement already satisfied: pytz>=2022.5 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from yfinance) (2024.1)
Collecting frozendict>=2.3.4 (from yfinance)
Downloading frozendict-2.4.6-py312-none-any.whl.metadata (23 kB)
Collecting peewee>=3.16.2 (from yfinance)
Downloading peewee-3.17.9.tar.gz (3.0 MB)
?25l ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/3.0 MB ? eta -:--:--
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.0/3.0 MB 131.9 MB/s eta 0:00:00
?25h
Installing build dependencies ... ?25l-
\
|
done
?25h Getting requirements to build wheel ... ?25l- done
?25h Preparing metadata (pyproject.toml) ... ?25l-
done
?25hRequirement already satisfied: beautifulsoup4>=4.11.1 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from yfinance) (4.12.3)
Requirement already satisfied: soupsieve>1.2 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from beautifulsoup4>=4.11.1->yfinance) (2.5)
Requirement already satisfied: python-dateutil>=2.8.2 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from pandas>=1.3.0->yfinance) (2.9.0.post0)
Requirement already satisfied: tzdata>=2022.7 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from pandas>=1.3.0->yfinance) (2023.3)
Requirement already satisfied: charset-normalizer<4,>=2 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from requests>=2.31->yfinance) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from requests>=2.31->yfinance) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from requests>=2.31->yfinance) (2.2.3)
Requirement already satisfied: certifi>=2017.4.17 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from requests>=2.31->yfinance) (2024.8.30)
Requirement already satisfied: six>=1.5 in /home/runner/miniconda3/envs/quantecon/lib/python3.12/site-packages (from python-dateutil>=2.8.2->pandas>=1.3.0->yfinance) (1.16.0)
Downloading yfinance-0.2.55-py2.py3-none-any.whl (109 kB)
Downloading frozendict-2.4.6-py312-none-any.whl (16 kB)
Downloading multitasking-0.0.11-py3-none-any.whl (8.5 kB)
Building wheels for collected packages: peewee
Building wheel for peewee (pyproject.toml) ... ?25l-
\
|
done
?25h Created wheel for peewee: filename=peewee-3.17.9-cp312-cp312-linux_x86_64.whl size=303871 sha256=debeecdc8f2e92da85d2b3fc728dfe8addc9e1b7319157841dd8da27cbcfd2c2
Stored in directory: /home/runner/.cache/pip/wheels/43/ef/2d/2c51d496bf084945ffdf838b4cc8767b8ba1cc20eb41588831
Successfully built peewee
Installing collected packages: peewee, multitasking, frozendict, yfinance
Successfully installed frozendict-2.4.6 multitasking-0.0.11 peewee-3.17.9 yfinance-0.2.55
我们将使用以下导入
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
from scipy.optimize import brentq
from scipy.stats import beta
import matplotlib as mpl
FONTPATH = "fonts/SourceHanSerifSC-SemiBold.otf"
mpl.font_manager.fontManager.addfont(FONTPATH)
plt.rcParams['font.family'] = ['Source Han Serif SC']
27.2. 数据#
下图显示了自 2016 年初以来以美元计价的棉花价格。
Show source
s = yf.download('CT=F', '2016-1-1', '2023-4-1', auto_adjust=False)['Adj Close']
Show output
[*********************100%***********************] 1 of 1 completed
Show source
fig, ax = plt.subplots()
ax.plot(s, marker='o', alpha=0.5, ms=1)
ax.set_ylabel(r'棉花价格(美元)', fontsize=12)
ax.set_xlabel(r'日期', fontsize=12)
plt.show()
该图显示了棉花价格的巨大波动,令人惊讶。
是什么导致了这些波动?
一般来说,价格取决于以下各方的选择和行为:
供应商,
消费者,以及
投机者。
我们的重点将是这些方之间的互动。
我们将通过一个动态的供需模型将它们联系在一起,称为 竞争性储存模型。
该模型由 [Samuelson, 1971]、[Wright and Williams, 1982]、[Scheinkman and Schechtman, 1983]、[Deaton and Laroque, 1992]、[Deaton and Laroque, 1996] 和 [Chambers and Bailey, 1996] 开发。
27.3. 竞争性储存模型#
在竞争性储存模型中,商品被视为一种资产。这些商品具有两个特点:
可以被投机者交易,并且
对消费者有内在价值。
总需求是消费者需求和投机者需求的总和。
供应是外生的,取决于“收成”。
Note
如今,基本的计算机芯片和集成电路等高度标准化的产品,在金融市场上也被视为商品。对这类商品来说,”收成”一词并不太恰当。
不过为了简化问题,我们还是沿用这个术语。
均衡价格是通过竞争决定的。
它是当前状态的一个函数(决定当前的收成并预测未来的收成)。
27.4. 模型#
考虑一个单一商品的市场,其价格在时间
该商品在时间
我们假设序列
投机者可以在各期之间储存该商品,当前期购买的
这里的参数
为了简化问题,风险自由利率取为零,因此购买
其中
27.5. 均衡#
在本节中,我们定义均衡并讨论如何计算它。
27.5.1. 均衡条件#
假设投机者是风险中性的,这意味着他们在预期利润为正时会购买商品。
因此,如果预期利润为正,则市场不处于均衡状态。
所以要达到均衡,价格必须满足“无套利”条件:
这表明当预期价格低于当前价格时,就不存在套利空间。
利润最大化给出了额外条件:
我们还要求市场出清,即每期供应等于需求。
假设消费者根据价格
令
关于数量:
供应是投机者的持有量和当前收成的总和,并且
需求是消费者购买和投机者购买的总和。
用数学语言表述:
供应由
给出,取值在 中。需求为
。
因此,市场均衡条件为:
初始条件
27.5.2. 一个均衡函数#
如何找到均衡呢?
我们的思路是寻找一个只依赖当前状态的价格系统。
(我们的解法使用了 拟设(ansatz),这是一种基于推测的猜想——在这里是对价格函数的猜想。)
具体来说,我们在
价格和数量随后满足:
我们选择
更准确地说,我们要找一个
其中
事实证明,这样的
要理解这一点,我们首先注意到:
因此,条件 (27.1) 要求:
这个不等式直接来自 (27.5)。
其次,对于 (27.2),假设:
那么根据 (27.5),我们得到
此时有
我们找到了一个均衡,验证了 ansatz。
27.5.3. 计算均衡#
现在我们知道,均衡可以通过找到一个满足 (27.5) 的函数
在温和的条件下,可以证明在
此外,我们可以通过逐次逼近来计算这个函数。
具体来说,我们从一个初始函数猜测开始,然后使用 (27.5) 来更新它。
这会生成一系列函数
我们继续这个过程,直到它收敛,即
然后,我们将最终计算得到的
为了实现更新步骤,将 (27.5) 和 (27.6) 结合起来是很有帮助的。
这给出了更新规则:
换句话说,我们将
实际上,我们无法对每个
对这些点,我们可以得到相应的值
然后,我们在网格点
我们不断重复这个过程,直到结果收敛。
27.6. 代码#
下面的代码实现了这个迭代过程。我们从
我们选择一个偏移的贝塔分布作为分布
(27.8) 中的积分通过 Monte Carlo 方法来计算。
α, a, c = 0.8, 1.0, 2.0
beta_a, beta_b = 5, 5
mc_draw_size = 250
gridsize = 150
grid_max = 35
grid = np.linspace(a, grid_max, gridsize)
beta_dist = beta(5, 5)
Z = a + beta_dist.rvs(mc_draw_size) * c # 随机冲击的观测值
D = P = lambda x: 1.0 / x
tol = 1e-4
def T(p_array):
new_p = np.empty_like(p_array)
# 插值以获得p 作为函数。
p = interp1d(grid,
p_array,
fill_value=(p_array[0], p_array[-1]),
bounds_error=False)
# 更新
for i, x in enumerate(grid):
h = lambda q: q - max(α * np.mean(p(α * (x - D(q)) + Z)), P(x))
new_p[i] = brentq(h, 1e-8, 100)
return new_p
fig, ax = plt.subplots()
price = P(grid)
ax.plot(grid, price, alpha=0.5, lw=1, label="反需求曲线")
error = tol + 1
while error > tol:
new_price = T(price)
error = max(np.abs(new_price - price))
price = new_price
ax.plot(grid, price, 'k-', alpha=0.5, lw=2, label=r'$p^*$')
ax.legend()
ax.set_xlabel('$x$')
ax.set_ylabel("价格")
plt.show()
上图显示了逆需求曲线
一旦我们得到了
# 将价格数组转化为价格函数。
p_star = interp1d(grid,
price,
fill_value=(price[0], price[-1]),
bounds_error=False)
def carry_over(x):
return α * (x - D(p_star(x)))
def generate_cp_ts(init=1, n=50):
X = np.empty(n)
X[0] = init
for t in range(n-1):
Z = a + c * beta_dist.rvs()
X[t+1] = carry_over(X[t]) + Z
return p_star(X)
fig, ax = plt.subplots()
ax.plot(generate_cp_ts(), label="价格")
ax.set_xlabel("时间")
ax.legend()
plt.show()