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}")
注意:zip 会在最短的列表处停止。如果两个列表长度不一致,多余的元素会被忽略。我曾经因为这个 bug 少算了一天的数据,回测结果看起来特别好,实盘就露馅了。

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)         # 关键字参数
核心原则:函数应该只做一件事,并且做好。如果一个函数超过 20 行,考虑拆分成更小的函数。这在量化策略里尤其重要——你后面调试的时候会感谢自己的。

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
我的建议:刚开始不用把 OOP 搞得太复杂。先学会用类来组织代码,把相关的函数和数据放在一起。等你需要写多个策略、多个数据源的时候,自然就理解继承和多态的价值了。

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)
注意:JSON 只支持字符串、数字、布尔值、列表、字典和 None。如果你有 datetime 对象或自定义类,需要先转换成 JSON 支持的类型。我一般用 isoformat() 把时间转成字符串再存。

3.5 本章知识体系

下面这张图把本章的核心内容串起来了。你可以看到控制流是基础,函数是组织单元,面向对象是更高层次的抽象,而标准库提供了日常需要的工具。

第三章:Python基础速通(二)知识体系 Python 核心编程 控制流 if / elif / else for + enumerate/zip while + break 保护 函数 def + 参数默认值 文档字符串 返回值与参数类型 面向对象 class + __init__ self 与实例方法 封装/继承/多态 常用标准库 os(路径/文件) datetime(时间处理) json(数据交换)

这一章的内容不少,但都是量化开发的日常工具。控制流让你能写逻辑判断,函数让你能复用代码,面向对象让你能组织复杂系统,标准库让你不用重复造轮子。把这些练熟了,后面写策略、做回测就会顺手很多。