跳转至

第 3 课 程序怎样记住价格、文字和判断结果

这一课只解决一个基础问题。人能凭上下文认出纸上的 10.20 是价格,程序需要更明确的名字、类型和单位,才能在后面正确计算。

适合谁刚运行过第一行 Python 的读者
前置知识知道代码需要交给 Python 执行
建议用时90 分钟,可以分两次完成
本课成果保存两天价格,算出变化率并检查单位

纸上的 10.20 为什么还不够

假设你在一张纸上只写了 10.20。第二天再看时,这个数字可能是开盘价、收盘价、手续费,也可能是持仓数量。人会根据记忆猜测上下文,Python 不会猜。程序必须知道它正在处理什么内容,这个内容叫什么名字,允许参加哪些运算。

Python 直接写出的数字、文字和真假判断都叫10.20 属于小数,"周一" 保存一段文字。条件成立时可以用真假值 True 表示。值可以直接参与运算,不过一段量化程序会反复引用价格、费率和现金,长期只写裸数字很容易混淆,所以我们要给值取名字。

close_price = 10.20

阅读这行代码时,从右向左理解会更自然。右边的 10.20 是准备保存的值,单个等号表示“把右边结果交给左边名字”,左边的 close_price 是以后取回这个值时使用的名字。这个有名字、可以在后续代码中继续使用的位置叫变量

flowchart LR
    accTitle: 一行赋值代码完成了什么
    accDescr: Python 先计算等号右边的值,再把结果交给左边变量名,后面的代码通过变量名取回当前值

    a[右边的值<br/>10.20] --> b[执行赋值<br/>把结果保存起来]
    b --> c[左边的变量名<br/>close_price]
    c --> d[后续计算<br/>继续读取当前值]

变量可以暂时理解成贴有名称的记录位置。这个比喻只帮助入门,程序内部还会管理对象和内存地址,本课暂时不用展开。当前最重要的习惯是让名字说明含义。看到 close_price,读者能猜到它保存收盘价;看到 x,几天以后连作者本人也可能忘记它代表什么。

Python 怎样区分数字和文字

下面两行在屏幕上都出现 10.20,Python 却把它们当成两类内容。第一行没有引号,保存的是可以参加数学运算的小数。第二行带有成对引号,引号告诉 Python 中间内容是一段文字。

price = 10.20
message = "10.20"

这种类别叫数据类型。类型决定一个值能够做什么。数值加 1 会得到新的数值,文字加另一段文字会把两段内容接在一起。下面代码先做数学加法,再做文字拼接。

print(price + 1)
print(message + " 元")

运行后会看到两行结果。第一行是 11.2,因为 10.20 与 1 进行了数学相加。第二行是 10.20 元,因为两段文字按顺序连接。若写成 message + 1,Python 无法决定应该做数学运算还是文字拼接,于是会停止并给出 TypeError。这类报错在保护数据含义,它提醒我们先确认类型。

入门阶段先掌握四种类型就足够覆盖大部分示例。

类型 Python 名称 示例 量化中的常见用途
整数 int 5 天数、数量、行号
浮点数 float 10.20 价格、收益率、费率
文字 str "周一" 日期标签、资产名称、字段名
布尔值 bool True 某个条件是否成立

布尔值只有 TrueFalse 两种结果,第 5 课会让它决定程序走哪条路。现在可以用 type() 查看任何值的类型。type(10.20) 会得到浮点数,type("10.20") 会得到文字。遇到“这个数字为什么不能相加”的问题时,先打印类型往往比盯着代码猜测更有效。

print(type(10.20))
print(type("10.20"))

一段表达式怎样产生新值

变量保存旧值,运算会产生新值。假设某只虚构资产昨天收于 10 元,今天收于 10.50 元,我们先用减法得到价格变化,再用变化除以原价格得到收益率。开始写代码以前,先在纸上算一遍。价格增加 0.50 元,0.50 除以 10 等于 0.05,所以收益率为 5%。

yesterday_close = 10.00
today_close = 10.50

price_change = today_close - yesterday_close
return_rate = price_change / yesterday_close

print(price_change)
print(return_rate)

前两行保存原始输入,中间两行使用输入计算新值,最后两行只负责显示结果。运行后会依次看到 0.50.05。小数 0.05 与百分数 5% 表达同一个比例,量化程序通常用小数参加计算,因为乘除更直接;写报告时再把它格式化成人习惯阅读的百分数。

print(f"变化率为 {return_rate:.1%}")

这行代码会显示 变化率为 5.0%。开头的 f 允许文字中插入变量,花括号标出插入位置,.1% 要求按百分数显示并保留一位小数。格式化只改变显示方式,变量 return_rate 内部仍然保存 0.05,后续计算不会因此乘上一百。

收益率也可以直接写成 today_close / yesterday_close - 1。它与先算变化再除以原价格得到相同结果。初学阶段保留 price_change 这个中间变量更容易检查,因为你可以把 0.5 与纸面结果比较,发现错误时也知道问题出在减法还是除法。

变量为什么会随着程序更新

量化程序需要描述现金和持仓随交易变化,所以变量允许重新赋值。假设账户原有 1000 元,买入占用 120 元,第二行代码先读取旧现金,完成减法,再把结果保存回同一个变量名。

cash = 1000
cash = cash - 120
print(cash)

结果为 880。数学等式 cash = cash - 120 不可能成立,Python 的单个等号表达的是赋值动作,因此这一行没有矛盾。判断两边当前值是否相等时要写双等号 ==,例如 cash == 880 会得到 True。赋值与比较只差一个等号,却承担完全不同的任务。

为了看清账户变化,可以把更新拆成几步。下面例子先扣买入金额,再扣手续费,每一步以后都打印现金。若最终结果与手算不符,从第一处出现差异的位置向前检查即可。

cash = 1000.0
buy_amount = 300.0
fee = 0.3

cash = cash - buy_amount
print("买入后现金", cash)

cash = cash - fee
print("扣费后现金", cash)

运行后会先显示 700.0,再显示 699.7。同一个变量名只保留当前状态,旧的 1000 和 700 不会自动形成历史记录。完整回测需要把每天的现金、持仓和净值逐行保存到表格,这样才能回到某个日期解释变化。第 14 课会正式建立这种账本,本课只需要理解状态更新。

变量名和单位共同保护含义

Python 的变量名可以包含字母、数字和下划线,不能以数字开头。它还区分大小写,所以 pricePrice 会被当成两个名字。社区通常使用小写单词和下划线,例如 close_pricefee_rateholding_days,这套写法叫蛇形命名。

清楚的名字仍然不能自动携带单位。数值 5 可能表示五元、五天、五股或 5%。研究者要在变量名、表格列名和说明文字中明确单位。下面三个名字分别告诉读者天数、比例和人民币金额,代码复查时更容易发现数量级错误。

holding_days = 5
fee_rate = 0.0005
cash_yuan = 10000

fee_rate 的 0.0005 表示 0.05%。若误写成 0.05,程序会按 5% 扣费,两者相差一百倍。电脑会精确执行错误输入,所以“代码能运行”只能证明语法没有阻止它,不能证明金融含义正确。每个比例都应说明使用小数还是百分数,金额应说明货币,证券数量还要说明按股、按手还是按合约。

可以用 assert 设置一道简单检查。它会判断后面的条件,条件为假时立即停止,避免明显错误继续传到更远的计算。

assert 0 <= fee_rate < 0.01
assert cash_yuan >= 0

第一条把教学费率限制在 0% 到 1% 之间,第二条要求初始现金不为负。这里的范围只适用于本课示例,真实市场要根据品种和费用定义重新设置。assert 的价值来自提前写下预期,它不能替研究者决定什么范围合理。

文字数字怎样安全地变成数值

从 CSV 文件或网页读入的数据有时会被当成文字。"10.20" 看起来像价格,类型仍然是 str。确认内容确实是合法数字以后,可以用 float() 转成小数;数量字段需要整数时,可以用 int(),但要先确认是否允许小数。

raw_price = "10.20"
price = float(raw_price)
next_price = price + 0.30

print(next_price)

结果为 10.5。转换可能失败,例如 float("停牌") 会报错,因为“停牌”不是数值。此时应保留原始值并按数据规则处理,不能随手改成零。零价格是一条真实数值,会让后续收益率出现巨大变化;缺失或停牌则是在说明当时没有正常可用价格,两者含义不同。

小数为什么偶尔多出很长的尾巴

电脑使用二进制保存大多数浮点数,有些十进制小数无法被有限位二进制精确表示。运行 print(0.1 + 0.2) 时,某些环境会显示 0.30000000000000004。末尾差异来自表示方式,不代表 Python 把加法规则算错了。

研究一般收益率时,这种误差通常远小于行情本身的价格精度。需要精确处理结算金额、最小报价单位或货币分时,可以把金额换成整数最小单位,或者使用十进制类型并遵守交易所和账户的舍入规则。比较两个浮点计算结果时,也常检查差距是否足够小。

difference = abs((0.1 + 0.2) - 0.3)
print(difference < 1e-9)

abs() 取绝对值,1e-9 表示十亿分之一。若差距小于这个容许范围,判断结果为 True。容许范围应根据问题选择,价格最小跳动和资金规模都会影响合理精度,不能把一个固定数字复制到所有项目。

把本课知识连成一笔完整计算

现在用一段小程序把类型、变量、运算、格式化和检查接起来。输入是两天虚构收盘价与费率,目标是计算未扣费收益和扣除一次费用后的近似净收益。代码出现以前已经知道纸面答案,价格从 20 元涨到 21 元,毛收益为 5%,费率 0.05%,近似净收益为 4.95%。

old_close = 20.0
new_close = 21.0
fee_rate = 0.0005

assert old_close > 0
assert new_close > 0
assert 0 <= fee_rate < 0.01

gross_return = new_close / old_close - 1
net_return = gross_return - fee_rate

print(f"毛收益 {gross_return:.2%}")
print(f"近似净收益 {net_return:.2%}")

前三行只保存输入,三条检查拦住明显异常,中间两行计算结果,最后两行负责显示。输出应为“毛收益 5.00%”和“近似净收益 4.95%”。这里为了理解变量,只扣了一次比例费用,没有考虑买卖两端、最低费用、价差和滑点,因此它还不是完整交易成本模型。读者能说出这个限制,比得到漂亮数字更重要。

容易弄错的地方

容易混淆的写法 Python 会怎样理解 应该怎样检查
"10.5" 当成价格直接相加 引号让内容成为文字 先用 type() 查看,确认来源后再转换
=== 混用 单等号保存,双等号比较 用一句自然语言翻译当前代码
把 0.05 读成 0.05% 程序中的 0.05 通常表示 5% 在变量名或说明中写明比例口径
px1 保存多种价格 代码能运行,含义会迅速丢失 使用 open_priceclose_price 一类名称
浮点尾数不整齐便直接四舍五入原数据 显示问题和业务精度被混在一起 先确定最小价格单位与结算规则

一分钟自测

price = "12" 里的 price 能直接除以 2 吗?如果不能,安全的下一步是什么?

查看答案 它保存的是文字,直接除法会产生类型错误。先确认文字确实表示合法价格,再用 `float(price)` 转成数值。若原内容是“停牌”或空白,应按数据缺失规则处理,不能强行变成零。

本课术语

词语 现在应该怎样理解
程序正在处理的一项具体内容
变量 保存当前值并供后续代码引用的名字
数据类型 值所属的类别,它决定可以进行哪些操作
浮点数 Python 中常用来表示价格和收益率的小数类型
布尔值 只表示条件成立或不成立的真假类型
赋值 先计算等号右边,再把结果保存到左边名字
表达式 由值、变量和运算符组成并能产生结果的代码
格式化 改变数值展示方式而不改变内部值

课后练习

1 判断类型

"周五"2020.5False 分别属于什么类型?

提示观察引号、小数点和真假单词。
答案它们依次是文字 `str`、整数 `int`、浮点数 `float` 和布尔值 `bool`。

2 手算变化率

价格从 50 元变为 52 元,变化率是多少?请同时写出小数和百分数。

提示先算变化 2 元,再除以原来的 50 元。
答案`(52 - 50) / 50 = 0.04`,小数形式为 0.04,百分数形式为 4%。

3 找出错误

total = "100" + 20 为什么不能运行?在什么前提下可以修改为数值相加?

提示检查等号右边两项的数据类型。
答案`"100"` 是文字,`20` 是整数。确认文字确实表示数值以后,可以写 `float("100") + 20`,结果为 120。

4 动手修改

把完整示例中的费率改成 0.1%,先写出小数形式,再预测新的近似净收益。

提示0.1% 需要先除以 100,毛收益仍为 5%。
答案费率写成 `0.001`,近似净收益为 `0.05 - 0.001 = 0.049`,显示为 4.90%。

5 开放思考

为什么量化代码值得使用 close_price 这样的长名字,并同时在说明中写清单位?

评价要点回答应提到几个月后重新阅读、多人复查、开盘与收盘混用、比例数量级错误,以及代码能运行但金融含义仍可能错误。

下一课会把一个价格扩展成一组有顺序的行情,并解释列表、字典、索引和表格之间的关系。继续阅读第 4 课 怎样保存一组行情