一课掌握金融所需的 Python 基础:数据类型、列表、字典、循环、函数与错误信息
学习交易者所需的最少核心 Python 知识:数字、字符串、列表、字典、循环、函数,以及如何阅读回溯信息(traceback)。
你将学到什么
- 你在价格数据中会遇到的数据类型:
int、float、str、bool、None和datetime。 - 列表和字典如何存储交易清单,以及
for循环如何处理它。 - 如何编写带有文档字符串和默认参数的函数,以及为什么函数能让回测变得可测试。
- 如何自下而上阅读 Python 回溯信息,并修复五种最常见的错误。
讲解
类型。Python 中的每个值都有一个类型。42 是 int(整数),42.5 是 float(以二进制存储的小数),"AAPL" 是 str(文本),True 是 bool,而 None 表示“没有值”。浮点数是近似值(0.1 + 0.2 会输出 0.30000000000000004),因此永远不要用 == 比较它们;请使用 round() 或一个容差。日期请使用 pandas 时间戳(第 3 课)。
列表是用方括号书写的有序集合:closes = [100.0, 102.0, 101.5]。索引从零开始(closes[0] 是 100.0),负索引从末尾倒数(closes[-1] 是最新的收盘价),而切片 closes[1:3] 返回元素 1 和 2,但不包括 3。同样的“不含终点”规则也适用于 pandas 的 iloc。
字典把键映射到值:trade = {"symbol": "AAA", "entry": 50.0, "qty": 100}。用 trade["entry"] 访问;缺失的键会引发 KeyError,而 trade.get("fee", 0.0) 会返回一个默认值。
循环与条件。for 循环会依次访问每个元素:for t in trades: 后面跟一个缩进的代码块。缩进(四个空格)决定了代码块的范围。if、elif 和 else 根据条件进行分支,比较运算符有 ==、!=、<、>=。
函数把逻辑封装起来,以便复用和测试。关键字 def 开始一个定义,参数放在括号中,return 把值返回:
def r_multiple(entry, exit_price, stop, side="long"):
'''返回以初始风险(R)为单位的盈亏。'''
risk = abs(entry - stop)
pnl = exit_price - entry if side == "long" else entry - exit_price
return pnl / risk
三引号字符串是文档字符串(docstring,即说明文档),side="long" 是一个默认参数。让函数保持短小、明确其输入且没有副作用;正是这一点让第 9 课能够对它们进行单元测试。
阅读错误信息。出错时,Python 会打印一段回溯信息(traceback)。先读最后一行:它给出错误类型和错误消息。然后看它上方的那一行,那里显示错误发生在你代码中的哪个位置。最常见的错误有:NameError(你在定义变量之前就使用了它,常见原因是某个笔记本单元格没有运行);TypeError(例如把来自 CSV 文件的字符串与数字相加,用 float() 修复);KeyError(某个列名或字典键的拼写不同,检查空格和大小写);IndexError(你在一个只有 10 个元素的列表中索取第 10 个元素,记住索引从零开始);以及 ZeroDivisionError(止损价等于入场价时风险为零)。
实例演算
假设示例:三笔已平仓的多头交易,均以欧元计,不计佣金和税费。
trades = [
{"symbol": "AAA", "entry": 50.0, "exit": 53.0, "stop": 48.0, "qty": 100},
{"symbol": "BBB", "entry": 20.0, "exit": 19.0, "stop": 19.0, "qty": 250},
{"symbol": "CCC", "entry": 80.0, "exit": 78.0, "stop": 76.0, "qty": 60},
]
total = 0.0
for t in trades:
pnl = (t["exit"] - t["entry"]) * t["qty"]
r = r_multiple(t["entry"], t["exit"], t["stop"])
total += pnl
print(t["symbol"], pnl, round(r, 2))
print("Total:", total)
AAA 盈利 = (53 − 50) × 100 = €300;每股风险 €2,因此 R = 3 ÷ 2 = 1.5。BBB = (19 − 20) × 250 = −€250;每股风险 €1,R = −1.0(恰好被止损)。CCC = (78 − 80) × 60 = −€120;风险 €4,R = −0.5。合计 = 300 − 250 − 120 = −€70。输出各行:AAA 300.0 1.5、BBB -250.0 -1.0、CCC -120.0 -0.5、Total: -70.0。
total += pnl 是 total = total + pnl 的简写。三笔交易无法说明一种方法的好坏;这里的重点是运作机制。
常见错误
trades 列表却只重新运行循环单元格,一个过时的值就会悄无声息地进入结果;而在内核重启之后,你则会得到 NameError。在相信任何数字之前,请使用“Restart kernel and run all cells”(重启内核并运行所有单元格),确保它能从上到下无错误地运行完毕。练习活动
在纸上编写一个函数 position_size(account, risk_pct, entry, stop),它返回整数股数(向下取整),并在止损价等于入场价时引发一个清晰的 ValueError。然后写出账户 €8,000、风险 1.25%、入场价 €32.50、止损价 €30.00 时的结果。最后,说出如果你用 entry="32.50"(一个字符串)调用该函数且没有转换类型,Python 会引发哪种错误类型。
显示答案
def position_size(account, risk_pct, entry, stop):
per_share = abs(entry - stop)
if per_share == 0:
raise ValueError("止损价必须与入场价不同")
return int(account * risk_pct // per_share)
数字:风险预算 = 8,000 × 0.0125 = €100;每股风险 = 32.50 − 30.00 = €2.50;100 ÷ 2.50 恰好等于 40 股,因此函数返回 40(计划风险 €100,仓位价值 €1,300)。
使用 entry="32.50" 时,表达式 "32.50" - 30.0 会引发 TypeError: unsupported operand type(s) for -: 'str' and 'float'。请用 float(entry) 进行转换。
局限性:该函数忽略了最小交易单位、佣金,以及跳空可能造成超过计划 €100 的亏损这一事实。
要点回顾
- 浮点数是近似值,索引从零开始,切片不含终点;这三个事实可以避免大多数初学者的错误。
- 在使用 pandas 之前,由字典组成的列表是存放交易记录的自然形式;循环负责处理它,函数为它提供可复用的逻辑。
- 编写带文档字符串的短小纯函数;它们就是你将在第 9 课中测试的单元。
- 自下而上阅读回溯信息,并在相信笔记本结果之前先重启并运行全部单元格。
下一步
第 3 课介绍 pandas:Series、DataFrame、DatetimeIndex、选择和筛选行,以及用 resample 把日线转换为周线。
自测
回答问题后核对答案。你可以随意重试多次。
供您自行复习的自动自测。您的答案在浏览器中核对,我们不会查看或评估。这不是考试,也不是资格证书。
您的进度
“已完成”仅表示您将本课标记为已学完,并不是对您理解程度的测试。 学习进度和笔记仅保存在此设备的此浏览器中,而非账户中。