国内金价刚破 1000 元/克,但「涨了多少」和「该买多少」是两门数学

一条新闻,把我们领进三门数学
8 月 24 日那天,我刷朋友圈看到三件事:
- 第一条是「国内金价首次破 1000 元/克」的推送,配图是上海黄金交易所的收盘截图,显示 1004.21 元/克。
- 第二条是同事在群里说「黄金这么猛,我加仓了」。
- 第三条是另一个人在底下回:「涨了这么多,要不要追?买多少合适?」
这三个问题不是同一个问题。第一个问的是「涨了多少」,第二个问的是「明天还会涨吗」,第三个问的是「我该买多少」。第一个是积分的活儿,第二个是泰勒展开的活儿,第三个是带约束优化的活儿。
下面我就拿真实数据,一门一门地把它们拆开。
数据来源:上海黄金交易所 Au99.99 收盘价(2026-07-21 至 2026-08-24,共 25 个交易日,逐日公开),以及过去几年的年均价(约)。每张图都用 Python 从 CSV 重新画,可复现。
1.怎么算「涨了多少」
我从上金所拉了 7 月 21 日到 8 月 24 日共 25 个交易日的逐日 Au99.99 收盘价。长周期的对照锚点用过去几年的年均价。先看一眼全貌:

上半张是 2022 → 2025 的年均价,外加 2026 年截至 8 月 24 日的收盘快照。四年前均价 398,今年冲到 1004,整整翻了 2.5 倍。下半张是这一个多月的逐日线,前两周在 880 上下晃,从 8 月初开始加速,8 月 24 日收盘终于破千。
别把每天的涨幅简单加起来
这段时间到底涨了多少,最直觉的算法是把每天的日收益(今天除以昨天减 1)一路加总。
我跑了一遍:
真实累计涨幅=1004.21÷886.95−1=13.22
- 7/21 收盘 886.95 元,8/24 收盘 1004.21 元。。
- 但如果把每天的日收益简单相加,得到的是 12.60%。
- 差了 0.62 个百分点,看上去不大,可方法上是错的。
因为「加日收益」忽略了一件小事:今天赚的 1%,明天也是按今天涨完之后的本金再算的,也就是常说的复利。正确的做法是把每一天的收益真正"攒"起来,得用对数收益:
每天算一个 ln(今天÷昨天),一路累加。

图里那两条线,简单相加是 12.60%,对数收益求和转回来恰好是 13.22%,和真实累计一模一样。
这就是牛顿-莱布尼茨公式的离散版本。连续微积分里有一条:
∫(P′/P)dt=lnP+C
累计量等于「相对变化率」的积分。换成离散,就是:
「每天的相对变化」=对数收益ln(Pt/Pt−1)
一路加总 = ln(真实累计比),再 exp 回来 = 真实累计涨幅。
三百年这条公式解决的就是这类「把变化攒成总量」的问题。黄金的逐日涨跌,只是它的一份应用题。所以: 算累计收益,先把每天的涨幅取对数再相加,再取指数。这才是精确版本。
2.怎么猜「明天还会涨吗」
每天收盘,你会拿到三个数:今天的价格、昨天到今天的涨幅、过去几天的趋势。朴素做法是"昨天涨了 X%,明天大概也涨 X%",也就是用一个固定斜率往明天平推。
数学上有个更体面的写法,叫泰勒展开。挑一个「展开点」 t0=8月12日,在那一点附近,把价格 P(t) 写成:
P(t0+h)≈P(t0)+P′(t0)h+2!1P′′(t0)h2+3!1P′′(t0)h3+⋯
每一阶项的分母里都藏着一个阶乘 n!。0 阶是常数,1 阶是线性,2 阶开始抓曲率,3 阶抓曲率的变化。阶越高,分母的阶乘越大,远处那一项衰减得越快。这就是为什么"阶越多、展开点附近越准,但远距离不一定更稳"。
我拿真实的 8 月 12 日算了一下:
- 当天收盘 P0=955.75元。
- 一阶斜率 P′=(949.24−946.70)/2≈1.27元/天(轻微上行)。
- 二阶曲率 P″=949.24−2×955.75+946.70≈−15.56(轻微下凹,也就是拐弯朝下)。
把这三个数代回去,看 8 月 12 日往后 ±2 天,0/1/2 阶泰勒分别说什么:
- 0 阶是一条平直的黄点线:明天还是 955.75 元。
- 1 阶是一条倾斜的蓝虚线:明天 957.02 元,后天 958.29 元,直线往上爬。
- 2 阶是一条带弯曲的绿点划线:在 8 月 13 日几乎完美贴合实际(绿点和黑点重合在 949.24),但到 8 月 14 日就被拉到了 927 元附近,跟实际 940.72 元明显脱钩。 图 3:在 8/12 处做泰勒展开,向左右 ±2 天展示 0/1/2 阶逼近曲线,分母的 0!/1!/2! 标注在图例里。

真相是:8 月 13 日金价小幅下跌,8 月 14 日又继续小幅下跌,和朴素"线性外推往上走"的方向完全相反。一阶泰勒在拐弯处立刻走偏;二阶在展开点附近贴合得很好(这是泰勒的本性),但远离展开点就开始飘了。
这件事的实操启示
我并不打算用泰勒来预测明天金价。但它告诉我两件事: 第一,"看斜率就押方向"的朴素预测,遇到拐弯就翻车,一周前的趋势不是后一周的剧本。 第二,越靠近今天的信息越值钱,越远越贬值。这正好就是阶乘 n! 替我们做的事,它把远处的不可靠悄悄压低了。
3.怎么定「该买多少」
把"追涨"翻译成一个数学题
光知道涨了 13% 不够用,到底买不买、买多少?直觉的回答是"涨了加仓、跌了减仓"。但这个直觉没法对抗一个事实:你永远不知道明天是涨是跌。
一个更稳的思路是把"买多少"当成一道带约束的优化题:
在「我想赚 X%」的目标下,把波动压到最小。
波动(风险)用什么量?两个资产组合的方差有现成公式:
σp2=w2σg2+(1−w)2σs2+2w(1−w)ρσgσs
我们要做的,是在「收益达到某个目标」的约束下,让 σₚ² 取最小。这正是拉格朗日乘子法登场的地方:给约束乘一个系数 λ,再联立求导,最后写出闭式解。
我用真实黄金数据算了一次。先把每个参数摆清楚
这两类资产我只演示方法,不构成任何投资建议:
- 黄金:用 7/21 到 8/24 这 25 个交易日折算出年化收益 ≈ 130.4%、年化波动 ≈ 18.4%。注意这是把一个月出头当一年来年化,现实中不会持续,仅作示例。
- 股票指数:用一组假设参数:年化收益 8%、年化波动 18%、与黄金的相关性 0.1(低相关,避险资产的常见设定)。

蓝色那条弯成"香蕉形"的曲线就是两资产有效前沿,把权重 w 从 0 调到 1,每一个组合在「年化波动 σₚ」和「年化收益 μₚ」平面上都对应一个点,能取到的最优组合连起来就是这条曲线。绿星标的就是最小方差组合点,黄金权重 w* ≈ 0.487,约一半黄金、一半股票。
把这条曲线翻译成大白话:
- 想要更高的年化收益,就得接受更大的波动,没有"高收益低风险"的免费午餐;
- 想把波动压到最小,就得到绿星那个组合,约五五开;
- 黄金和股票相关性低(ρ = 0.1),意味着两个资产的涨跌不太同步,这就是黄金在组合里值钱的原因,它让你能拿到"同样的收益、更低的波动",或"同样的波动、更高的收益"。
拉格朗日乘子法并不是帮你预测金价,而是帮你回答一个更实际的问题:在不知道明天涨不涨的前提下,手里这点钱该按什么比例放进不同资产。它不能让你不亏钱,但能让你的风险结构变得清楚。
总结一下
标题里我问了两个问题:
- 涨了多少,上个月真实涨幅是 13.22%,但更关键的是怎么算:把每天的对数收益加总,再取指数。这条路叫牛顿-莱布尼茨的离散版本,原理就是把"每天的变化率"攒成"总累计量"。
- 该买多少,没有万能答案,但怎么想:把"想赚多少"翻译成"在某个收益目标下把波动压到最小",由拉格朗日乘子法给出闭式组合。这条路把"拍脑袋"换成了"算清楚"。
明天还会涨吗,泰勒展开告诉我的是一件更诚实的事:我们手里的信息越远越贬值,阶乘 n! 已经替我们把那种不可靠悄悄压低了,根据我们现有的信息来预测往往是不靠谱的.
金价破千也许是一时的,但它把我们领进的这三门数学,是处理任何时间序列、任何不确定未来的通用工具。
如果大家有更好的计算方法,或者有关于数学知识的新理解,欢迎评论区讨论哦。写文不易,三连支持下作者吧,你是大好人。
方法与可复现
代码:make_gold_figures.py(matplotlib + numpy,受管 venv 跑)。依赖:matplotlib、numpy。
python
# -*- coding: utf-8 -*-
import csv
from datetime import datetime
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib import font_manager as fm
# ---------- 字体 ----------
FONT = "Heiti SC"
if not any(f.name == FONT for f in fm.fontManager.ttflist):
FONT = "PingFang SC"
plt.rcParams["font.family"] = FONT
plt.rcParams["axes.unicode_minus"] = False
plt.rcParams["font.size"] = 12
# ---------- Okabe-Ito 色盲安全配色 ----------
C = {
"orange": "#E69F00", "sky": "#56B4E9", "green": "#009E73",
"yellow": "#F0E442", "blue": "#0072B2", "verm": "#D55E00",
"purple": "#CC79A7", "black": "#000000",
}
BASE = "运行结果存放路径"
def load_daily():
dates, closes = [], []
with open(f"数据文件路径1", encoding="utf-8-sig") as f:
for row in csv.DictReader(f):
dates.append(datetime.strptime(row["date"], "%Y-%m-%d"))
closes.append(float(row["close_yuan_per_g"]))
return np.array(dates), np.array(closes, float)
def load_annual():
yrs, avgs, notes = [], [], []
with open(f"数据文件路径2", encoding="utf-8-sig") as f:
for row in csv.DictReader(f):
yrs.append(int(row["year"])); avgs.append(float(row["avg_yuan_per_g"]))
notes.append(row["note"])
return np.array(yrs), np.array(avgs, float), notes
def style(ax):
ax.spines["top"].set_visible(False); ax.spines["right"].set_visible(False)
ax.grid(True, alpha=0.25, lw=0.6)
ax.tick_params(length=0)
dates, P = load_daily()
yrs, avgs, _ = load_annual()
# =====================================================================
# fig1:金价破千全景(上:长周期年均;下:逐日 7/21--8/24 标出千元线)
# =====================================================================
fig, (a1, a2) = plt.subplots(2, 1, figsize=(8, 7), dpi=150)
fig.subplots_adjust(hspace=0.32, left=0.10, right=0.95, top=0.93, bottom=0.07)
# 上:长周期年均
a1.plot(yrs, avgs, "-o", color=C["verm"], lw=2.4, ms=7, zorder=3)
for x, y in zip(yrs, avgs):
a1.annotate(f"{y:.0f}", (x, y), textcoords="offset points", xytext=(0, 9),
ha="center", fontsize=10, color=C["black"])
a1.axhline(1000, color=C["purple"], ls="--", lw=1.4)
a1.annotate("1000 元/克", (yrs[-1], 1000), color=C["purple"], fontsize=9,
xytext=(-4, -12), textcoords="offset points", ha="right")
a1.set_title("上金所 Au99.99 年均价:四年爬坡,2026 破千", fontsize=13, weight="bold")
a1.set_ylabel("元/克")
a1.set_xticks(yrs)
a1.set_ylim(300, 1120)
style(a1)
# 下:逐日
a2.plot(dates, P, "-o", color=C["blue"], lw=2.2, ms=4, zorder=3, label="Au99.99 收盘")
a2.axhline(1000, color=C["purple"], ls="--", lw=1.4, label="1000 元/克")
i_break = int(np.argmax(P >= 1000))
a2.scatter([dates[i_break]], [P[i_break]], color=C["verm"], s=90, zorder=5)
a2.annotate(f"8/24 收盘 {P[i_break]:.2f}\n国内首次破千", (dates[i_break], P[i_break]),
xytext=(-10, -42), textcoords="offset points", ha="right", fontsize=9.5,
color=C["verm"], arrowprops=dict(arrowstyle="-", color=C["verm"], lw=1))
a2.set_title("2026 年 7/21--8/24 逐日收盘:从 887 冲到 1004", fontsize=13, weight="bold")
a2.set_ylabel("元/克"); a2.set_ylim(860, 1030)
a2.tick_params(axis="x", rotation=0)
import matplotlib.dates as mdates
a2.xaxis.set_major_formatter(mdates.DateFormatter("%m/%d"))
a2.legend(frameon=False, loc="lower right")
style(a2)
fig.savefig(f"图片路径1", dpi=300)
print("fig1 done")
# =====================================================================
# fig2:日收益 → 累计(牛顿莱布尼茨离散版 / 对数收益)
# =====================================================================
simp_ret = P[1:] / P[:-1] - 1.0 # 简单日收益率
log_ret = np.log(P[1:] / P[:-1]) # 对数日收益率
cum_simple = np.cumsum(simp_ret) # 简单相加(会高估)
cum_log = np.cumsum(log_ret) # 对数相加 = 真实累计对数收益
true_cum = P[-1] / P[0] - 1.0 # 真实累计涨幅
print(f"真实累计涨幅 = {true_cum*100:.2f}% 简单相加={cum_simple[-1]*100:.2f}% "
f"对数求和转回={ (np.exp(cum_log[-1])-1)*100:.2f}%")
fig, (b1, b2) = plt.subplots(2, 1, figsize=(8, 7), dpi=150)
fig.subplots_adjust(hspace=0.32, left=0.10, right=0.95, top=0.93, bottom=0.07)
xd = dates[1:]
b1.bar(xd, simp_ret * 100, color=C["sky"], width=0.6, zorder=2)
b1.axhline(0, color=C["black"], lw=0.8)
b1.set_title("每天涨多少?日收益率(变化率的快照)", fontsize=13, weight="bold")
b1.set_ylabel("日收益率 %")
b1.xaxis.set_major_formatter(mdates.DateFormatter("%m/%d"))
style(b1)
b2.plot(xd, cum_simple * 100, "-o", color=C["orange"], lw=2, ms=3,
label=f"简单日收益相加 = {cum_simple[-1]*100:.2f}%")
b2.plot(xd, (np.exp(np.cumsum(log_ret)) - 1) * 100, "-o", color=C["green"], lw=2.2, ms=3,
label=f"对数收益求和转回 = {(np.exp(cum_log[-1])-1)*100:.2f}%")
b2.axhline(true_cum * 100, color=C["purple"], ls="--", lw=1.4,
label=f"真实累计 = {true_cum*100:.2f}%")
b2.set_title("攒起来涨多少?简单相加是近似,对数收益求和才精确", fontsize=13, weight="bold")
b2.set_ylabel("累计涨幅 %"); b2.legend(frameon=False, fontsize=9.5)
b2.xaxis.set_major_formatter(mdates.DateFormatter("%m/%d"))
style(b2)
fig.savefig(f"图片路径2", dpi=300)
print("fig2 done")
# =====================================================================
# fig3:泰勒逐阶逼近(在 8/12 处展开,向左右外推 ±2 天)
# 说明:n 阶项分母有个 n!(0!=1, 1!=1, 2!=2)逐项出现;
# 阶越高、展开点附近贴合越好,远处仍会偏(这是泰勒的本性)。
# =====================================================================
t = np.arange(len(P))
t0 = int(np.where((dates == datetime(2026, 8, 12)))[0][0])
P0 = P[t0]
P1 = (P[t0 + 1] - P[t0 - 1]) / 2.0
P2 = (P[t0 + 1] - 2 * P[t0] + P[t0 - 1])
print(f"t0=8/12 P0={P0:.2f} P'={P1:.3f} P''={P2:.3f}")
# h ∈ [-2, +2],5 个点,看清"展开点附近谁更准"
left, right = 2, 2
tt = np.arange(t0 - left, t0 + right + 1)
PP = P[tt]
hh = tt - t0
T0 = np.full_like(hh, P0, float)
T1 = P0 + P1 * hh
T2 = P0 + P1 * hh + P2 * hh**2 / 2.0
fig, ax = plt.subplots(figsize=(8, 5.2), dpi=150)
fig.subplots_adjust(left=0.11, right=0.95, top=0.91, bottom=0.11)
ax.plot(tt, PP, "-o", color=C["black"], lw=2.6, ms=6, zorder=5, label="实际金价")
ax.plot(tt, T0, color=C["orange"], lw=1.8, ls=":", label="0 阶(常数,÷0!)")
ax.plot(tt, T1, color=C["sky"], lw=2.0, ls="--", label="1 阶(线性,÷1!)")
ax.plot(tt, T2, color=C["green"], lw=2.0, ls="-.", label="2 阶(含曲率,÷2!)")
ax.axvline(t0, color=C["verm"], ls=":", lw=1.2)
ax.annotate("展开点 8/12", (t0, P0), color=C["verm"], fontsize=10,
xytext=(8, 14), textcoords="offset points")
ax.annotate("线性外推走偏", (t0 + 1, T1[left + 1]),
xytext=(8, 14), textcoords="offset points", fontsize=9, color=C["sky"],
arrowprops=dict(arrowstyle="-", color=C["sky"], lw=0.8))
ax.annotate("二阶在展开点\n附近贴合", (t0 + 1, T2[left + 1] - 0.5),
xytext=(8, -42), textcoords="offset points", fontsize=9, color=C["green"],
arrowprops=dict(arrowstyle="-", color=C["green"], lw=0.8))
ax.set_title("只知道当下金价+斜率,怎样预判明天?泰勒展开逐阶逼近",
fontsize=12.5, weight="bold")
ax.set_ylabel("元/克")
labels = [dates[int(i)].strftime("%m/%d") for i in tt]
ax.set_xticks(tt); ax.set_xticklabels(labels)
ax.set_ylim(910, 970)
ax.legend(frameon=False, fontsize=9.5, loc="lower center")
style(ax)
fig.savefig(f"图片路径3", dpi=300)
print("fig3 done")
# =====================================================================
# fig4:两资产风险-收益前沿(拉格朗日求最小方差组合)
# =====================================================================
# 黄金:基于 7/21--8/24 窗口的真实测算(年化,仅示例)
mu_g = float(np.mean(log_ret) * 252)
sig_g = float(np.std(log_ret, ddof=1) * np.sqrt(252))
# 股票指数:示例参数(明确标注,非真实推荐)
mu_s, sig_s, rho = 0.08, 0.18, 0.10
print(f"黄金(窗口测算) 年化收益≈{mu_g*100:.1f}% 年化波动≈{sig_g*100:.1f}%")
w = np.linspace(0, 1, 201)
sig_p = np.sqrt(w**2 * sig_g**2 + (1 - w)**2 * sig_s**2 + 2 * w * (1 - w) * rho * sig_g * sig_s)
mu_p = w * mu_g + (1 - w) * mu_s
# 全局最小方差组合(拉格朗日闭式)
w_min = (sig_s**2 - rho * sig_g * sig_s) / (sig_g**2 + sig_s**2 - 2 * rho * sig_g * sig_s)
i_min = int(np.argmin(sig_p))
print(f"最小方差组合 黄金权重 w* = {w_min:.3f}")
fig, ax = plt.subplots(figsize=(8.4, 5.6), dpi=150)
fig.subplots_adjust(left=0.12, right=0.95, top=0.92, bottom=0.11)
ax.plot(sig_p * 100, mu_p * 100, color=C["blue"], lw=2.4, zorder=3, label="两资产有效前沿")
ax.scatter([sig_g * 100], [mu_g * 100], color=C["verm"], s=80, zorder=5)
ax.annotate("黄金", (sig_g * 100, mu_g * 100), xytext=(8, -4),
textcoords="offset points", fontsize=10, color=C["verm"])
ax.scatter([sig_s * 100], [mu_s * 100], color=C["orange"], s=80, zorder=5)
ax.annotate("股票指数(示例)", (sig_s * 100, mu_s * 100), xytext=(-8, 10),
textcoords="offset points", fontsize=10, color=C["orange"], ha="right")
ax.scatter([sig_p[i_min] * 100], [mu_p[i_min] * 100], color=C["green"], s=140, zorder=6,
marker="*")
ax.annotate(f"最小方差组合\n黄金 {w_min*100:.0f}% / 股票 { (1-w_min)*100:.0f}%",
(sig_p[i_min] * 100, mu_p[i_min] * 100), xytext=(14, -8),
textcoords="offset points", ha="left", fontsize=9.5, color=C["green"])
ax.set_title("想赚 X%,怎样把波动压到最小?带约束求最优(拉格朗日)", fontsize=12.5, weight="bold")
ax.set_xlabel("年化波动(风险) %"); ax.set_ylabel("年化收益(目标) %")
ax.legend(frameon=False, loc="lower center", fontsize=9.5, bbox_to_anchor=(0.5, 0.02))
style(ax)
fig.savefig(f"图片路径4", dpi=300)
print("fig3 done" if False else "fig4 done")
print("ALL FIGURES SAVED")