3. Python基础速通(二):控制流、函数、面向对象与常用标准库
上一章我们把 Python 的基本数据类型和容器过了一遍。说实话,光有那些还不够——你没法让代码根据条件走不同分支,也没法重复执行某段逻辑。这一章我们就把这些「骨架」搭起来:控制流、函数、面向对象,再加几个量化里天天用的标准库。
我个人习惯是,学新语言先看它的控制流怎么写,因为这直接决定了代码的组织方式。Python 的控制流很简洁,没有花括号,全靠缩进。刚开始可能不习惯,但写久了你会觉得——嗯,这其实挺舒服的。
3.1 控制流:if / for / while
3.1.1 if 条件判断
量化策略里到处都是条件判断:如果收盘价突破均线就买入,如果回撤超过 5% 就止损。Python 的 if 语法长这样:
price = 105
ma20 = 100
if price > ma20:
print("价格在均线上方,考虑买入")
elif price == ma20:
print("价格等于均线,观望")
else:
print("价格在均线下方,考虑卖出或持有")
注意冒号和缩进。我刚开始写的时候经常忘记冒号,然后报语法错误。还有一点:Python 没有 switch-case,多分支就用 elif 链。如果分支太多,可以考虑用字典映射,后面会讲到。
if 90 < price < 110:,这在其他语言里很少见,但 Python 支持。
3.1.2 for 循环
for 循环在量化里用的最多——遍历历史 K 线数据、遍历股票列表、遍历因子值。基本语法:
prices = [100, 102, 101, 105, 108]
for i, p in enumerate(prices):
print(f"第{i+1}天的价格是{p}")
enumerate 是我最常用的函数之一。它同时返回索引和值,省得你自己维护一个计数器。如果你只需要遍历值,直接 for p in prices 就行。
还有一个常用的是 zip,可以同时遍历多个列表:
dates = ["2024-01-01", "2024-01-02", "2024-01-03"]
prices = [100, 102, 101]
for date, price in zip(dates, prices):
print(f"{date} 收盘价: {price}")
3.1.3 while 循环
while 循环在量化里用的相对少一些,但某些场景很合适:比如等待某个条件满足,或者实现一个事件驱动的循环。基本语法:
balance = 10000
target = 20000
while balance < target:
profit = balance * 0.01 # 假设每天赚1%
balance += profit
print(f"当前资金: {balance:.2f}")
print("目标达成!")
用 while 一定要小心死循环。我建议在循环体里加一个计数器,超过最大次数就 break:
count = 0
max_iter = 1000
while condition and count < max_iter:
# 处理逻辑
count += 1
3.2 函数定义与使用
函数是代码复用的基本单元。在量化研究里,你会把策略逻辑、信号计算、风险控制都写成函数。Python 定义函数用 def:
def calc_sma(prices, window=20):
"""计算简单移动平均线"""
if len(prices) < window:
return None
sma = []
for i in range(window - 1, len(prices)):
window_data = prices[i - window + 1 : i + 1]
sma.append(sum(window_data) / window)
return sma
这里有几个要点:
- 参数默认值:
window=20,调用时可以省略 - 文档字符串:三引号里的内容,可以用
help()查看 - 返回值:可以返回多个值,实际上返回一个元组
Python 的函数参数很灵活。我个人最常用的是位置参数 + 关键字参数的组合:
def create_order(symbol, quantity, order_type="market", price=None):
print(f"下单: {symbol} {quantity}股, 类型:{order_type}")
if price:
print(f"限价: {price}")
# 调用方式
create_order("AAPL", 100) # 市价单
create_order("AAPL", 100, "limit", 150) # 限价单
create_order("AAPL", 100, price=150) # 关键字参数
3.3 面向对象编程入门
面向对象编程(OOP)在量化里无处不在。你用的回测框架、数据接口、策略模板,基本都是类和对象。说白了,类就是「蓝图」,对象就是根据蓝图造出来的「实物」。
看一个简单的策略类:
class MovingAverageStrategy:
"""双均线策略"""
def __init__(self, fast_window=5, slow_window=20):
self.fast_window = fast_window
self.slow_window = slow_window
self.position = 0 # 持仓状态
def generate_signal(self, prices):
fast_ma = sum(prices[-self.fast_window:]) / self.fast_window
slow_ma = sum(prices[-self.slow_window:]) / self.slow_window
if fast_ma > slow_ma and self.position == 0:
self.position = 1
return "BUY"
elif fast_ma < slow_ma and self.position == 1:
self.position = 0
return "SELL"
return "HOLD"
# 使用
strategy = MovingAverageStrategy(fast_window=10, slow_window=30)
signal = strategy.generate_signal([100, 102, 101, 105, 108])
这里 __init__ 是构造函数,在创建对象时自动调用。self 代表实例本身,所有实例方法第一个参数都是 self。我刚开始学的时候老忘记写 self,结果报错说参数数量不匹配——这个坑几乎每个人都踩过。
面向对象的三个核心特性:
- 封装:把数据和操作数据的方法打包在一起
- 继承:子类可以复用父类的代码
- 多态:同一个方法名在不同类中有不同实现
在量化里,继承用的很多。比如你有一个基础策略类,然后派生出均线策略、动量策略、均值回归策略:
class BaseStrategy:
def generate_signal(self, data):
raise NotImplementedError("子类必须实现此方法")
class MomentumStrategy(BaseStrategy):
def generate_signal(self, data):
# 动量策略实现
pass
3.4 常用标准库
Python 自带的库已经很强大了。量化里最常用的三个是 os、datetime 和 json。我几乎每天都会用到它们。
3.4.1 os 模块:文件和路径操作
处理数据文件、读取配置文件、创建目录,都离不开 os:
import os
# 获取当前工作目录
cwd = os.getcwd()
print(f"当前目录: {cwd}")
# 拼接路径(自动处理不同操作系统的分隔符)
data_path = os.path.join(cwd, "data", "daily")
if not os.path.exists(data_path):
os.makedirs(data_path) # 创建多级目录
# 列出目录下所有 CSV 文件
files = [f for f in os.listdir(data_path) if f.endswith(".csv")]
print(f"找到 {len(files)} 个数据文件")
我曾经犯过一个错误:直接用字符串拼接路径,结果在 Windows 上跑得好好的,部署到 Linux 服务器上就报错。用 os.path.join 就没事了。
3.4.2 datetime 模块:时间处理
量化离不开时间。K 线数据有时间戳,回测需要计算时间差,策略信号有时效性:
from datetime import datetime, timedelta
# 获取当前时间
now = datetime.now()
print(f"当前时间: {now}")
# 字符串转时间对象
date_str = "2024-01-15 09:30:00"
dt = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")
print(f"解析后: {dt}")
# 时间计算
next_day = dt + timedelta(days=1)
print(f"第二天: {next_day}")
# 计算两个时间差
delta = next_day - dt
print(f"相差: {delta.total_seconds()} 秒")
时间格式化是最容易出错的地方。我建议你记住几个常用格式:
| 格式符 | 含义 | 示例 |
|---|---|---|
%Y |
四位年份 | 2024 |
%m |
两位月份 | 01 |
%d |
两位日期 | 15 |
%H |
24小时制 | 09 |
%M |
分钟 | 30 |
%S |
秒 | 00 |
3.4.3 json 模块:数据交换
很多量化平台的数据接口返回 JSON 格式。配置文件也常用 JSON。Python 处理 JSON 非常简单:
import json
# 把 Python 对象转成 JSON 字符串
config = {
"strategy": "momentum",
"lookback": 20,
"stocks": ["AAPL", "MSFT", "GOOGL"],
"risk_limit": 0.02
}
json_str = json.dumps(config, indent=2)
print(json_str)
# 把 JSON 字符串转回 Python 对象
data = json.loads(json_str)
print(data["strategy"]) # 输出: momentum
# 读写文件
with open("config.json", "w") as f:
json.dump(config, f, indent=2)
with open("config.json", "r") as f:
loaded_config = json.load(f)
isoformat() 把时间转成字符串再存。
3.5 本章知识体系
下面这张图把本章的核心内容串起来了。你可以看到控制流是基础,函数是组织单元,面向对象是更高层次的抽象,而标准库提供了日常需要的工具。
这一章的内容不少,但都是量化开发的日常工具。控制流让你能写逻辑判断,函数让你能复用代码,面向对象让你能组织复杂系统,标准库让你不用重复造轮子。把这些练熟了,后面写策略、做回测就会顺手很多。