4、Python基础速通(下):文件读写、异常处理、面向对象编程入门、常用内置模块(datetime、os)

好,咱们接着上一章继续。上一章我们把Python的变量、流程控制、函数这些基本功过了一遍。这一章,我带你搞定几个真正写量化脚本时天天要用的东西:文件读写、异常处理、面向对象编程入门,还有两个特别常用的内置模块——datetime和os。

说实话,这几个知识点在量化交易里几乎每个脚本都会碰到。你想想看,读取历史行情数据是不是要文件读写?策略跑崩了是不是要异常处理?写一个交易策略类是不是要用面向对象?处理时间戳、管理文件路径,是不是离不开datetime和os?

好,咱们一个一个来。

4.1 文件读写:跟数据文件打交道

量化交易的第一步,往往是读取数据。CSV文件、TXT文件、JSON文件……Python的文件操作非常直观。我个人习惯用with open语句,因为它会自动帮你关闭文件,省心。

4.1.1 读取文本文件

# 读取一个CSV格式的行情文件
with open('stock_data.csv', 'r', encoding='utf-8') as f:
    content = f.read()
    print(content[:200])  # 只打印前200个字符看看

这里有个坑,我刚开始学的时候踩过——编码问题。Windows系统默认是gbk编码,而很多金融数据文件是utf-8编码。如果不指定encoding='utf-8',读出来全是乱码。嗯,这个一定要注意。

4.1.2 逐行读取大文件

如果行情数据文件特别大(比如几年的分钟线),一次性读入内存可能会爆掉。这时候逐行读取就派上用场了。

# 逐行读取,适合大文件
with open('big_data.csv', 'r', encoding='utf-8') as f:
    for line in f:
        # 处理每一行数据
        print(line.strip())  # strip()去掉换行符

我在处理美股历史tick数据时,一个文件经常几百MB,用逐行读取的方式,内存占用非常稳定。

4.1.3 写入文件

写入也很简单。比如你跑完一个回测,想把结果保存下来。

# 写入策略回测结果
results = [
    'date,return,sharpe\n',
    '2024-01-01,0.05,1.2\n',
    '2024-01-02,-0.02,0.8\n'
]

with open('backtest_results.csv', 'w', encoding='utf-8') as f:
    f.writelines(results)

注意模式参数:'w'是覆盖写入,'a'是追加写入。我曾经因为误用'w'模式,把辛苦跑了一天的回测结果给覆盖了……从那以后,我写文件前都会再三确认模式。

4.2 异常处理:让程序更健壮

量化交易中,程序跑着跑着突然崩溃,这是最让人头疼的。网络断了、数据格式变了、除零了……各种意外都可能发生。异常处理就是给程序穿上防弹衣。

4.2.1 try-except 基础用法

try:
    # 可能出错的代码
    price = float('abc')  # 这行会报错
    print(price)
except ValueError as e:
    print(f'数据格式错误:{e}')

你看,程序不会崩溃,而是优雅地打印出错误信息,然后继续运行。

4.2.2 捕获多种异常

实际项目中,一个操作可能引发多种异常。比如读取网络数据时,可能网络断开(ConnectionError),也可能数据格式不对(ValueError)。

try:
    # 从API获取行情数据
    data = fetch_market_data('AAPL')
    process_data(data)
except ConnectionError:
    print('网络连接失败,稍后重试')
except ValueError:
    print('数据格式异常,跳过该数据')
except Exception as e:
    print(f'未知错误:{e}')

我个人习惯把Exception放在最后,作为兜底。这样既能精确处理已知错误,又不会漏掉未知问题。

4.2.3 finally 和 else

finally块里的代码,不管有没有异常都会执行。常用于释放资源,比如关闭数据库连接。

try:
    file = open('data.csv', 'r')
    data = file.read()
except FileNotFoundError:
    print('文件不存在')
else:
    print('文件读取成功')  # 没有异常时执行
finally:
    file.close()  # 无论如何都要关闭文件
我的经验:在量化策略中,异常处理不是用来掩盖bug的,而是让程序在遇到意外时能优雅地降级或重试。比如网络请求失败,可以等待几秒后重试3次,而不是直接崩溃。

4.3 面向对象编程入门:把代码组织得更清晰

面向对象编程(OOP)听起来高大上,说白了就是把相关的数据和函数打包成一个“对象”。在量化交易中,一个交易策略、一个订单、一个账户,都很适合用对象来表示。

4.3.1 类和对象

类就是模板,对象就是根据模板造出来的具体实例。

class Stock:
    """股票类"""
    def __init__(self, symbol, price):
        self.symbol = symbol  # 股票代码
        self.price = price    # 当前价格
    
    def update_price(self, new_price):
        self.price = new_price
        print(f'{self.symbol} 价格更新为 {self.price}')

# 创建对象
aapl = Stock('AAPL', 150.0)
aapl.update_price(155.0)

__init__是构造方法,创建对象时自动调用。self代表对象本身,每个方法第一个参数都是它。

4.3.2 封装:把数据藏起来

好的设计应该把内部实现细节隐藏起来,只暴露必要的接口。在Python中,用双下划线开头的属性表示私有。

class TradingAccount:
    def __init__(self, initial_balance):
        self.__balance = initial_balance  # 私有属性
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
    
    def get_balance(self):
        return self.__balance

account = TradingAccount(10000)
account.deposit(5000)
print(account.get_balance())  # 15000
# print(account.__balance)  # 这行会报错

为什么要封装?你想想看,如果外部代码可以直接修改__balance,那账户余额就可能变成负数,这在交易系统中是不允许的。通过方法控制访问,可以加入校验逻辑。

4.3.3 继承:复用代码

不同的策略可能有共同的行为,比如都需要计算收益率。继承可以让我们复用这些公共代码。

class Strategy:
    def __init__(self, name):
        self.name = name
    
    def calculate_return(self, start_price, end_price):
        return (end_price - start_price) / start_price

class MovingAverageStrategy(Strategy):
    def __init__(self, name, window):
        super().__init__(name)  # 调用父类构造方法
        self.window = window
    
    def generate_signal(self, prices):
        # 计算移动平均线信号
        pass

ma_strategy = MovingAverageStrategy('MA_20', 20)
print(ma_strategy.calculate_return(100, 110))  # 0.1
核心理解:面向对象不是必须的,但当你写超过几百行的量化策略时,用OOP组织代码,维护起来会轻松很多。每个策略是一个类,每个订单是一个对象,逻辑清晰,不容易出错。

4.4 常用内置模块:datetime 和 os

这两个模块在量化交易中几乎天天用。datetime处理时间,os处理文件和路径。

4.4.1 datetime模块:时间就是金钱

量化交易中,时间戳、日期计算、时间格式转换,都是家常便饭。

from datetime import datetime, timedelta

# 获取当前时间
now = datetime.now()
print(f'当前时间:{now}')

# 格式化输出
formatted = now.strftime('%Y-%m-%d %H:%M:%S')
print(f'格式化后:{formatted}')

# 字符串转时间对象
date_str = '2024-01-15 09:30:00'
dt = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')
print(f'转换后:{dt}')

# 时间加减
yesterday = now - timedelta(days=1)
print(f'昨天此时:{yesterday}')

我在处理美股数据时,经常需要把Unix时间戳(比如1705300000)转换成可读的日期格式。用datetime.fromtimestamp()一行搞定。

4.4.2 os模块:管理文件和路径

量化策略经常要读取多个数据文件、创建日志目录、检查文件是否存在。os模块就是干这个的。

import os

# 获取当前工作目录
cwd = os.getcwd()
print(f'当前目录:{cwd}')

# 拼接路径(自动处理不同操作系统的分隔符)
data_path = os.path.join(cwd, 'data', 'stock_data.csv')
print(f'数据文件路径:{data_path}')

# 检查文件是否存在
if os.path.exists(data_path):
    print('文件存在')
else:
    print('文件不存在,请检查路径')

# 创建目录
log_dir = os.path.join(cwd, 'logs')
if not os.path.exists(log_dir):
    os.makedirs(log_dir)  # 创建多级目录
    print(f'创建目录:{log_dir}')

# 列出目录下所有CSV文件
csv_files = [f for f in os.listdir(data_path) if f.endswith('.csv')]
print(f'CSV文件列表:{csv_files}')
避坑指南:我曾经在Windows上写死路径分隔符\,结果代码放到Linux服务器上全崩了。用os.path.join()可以自动适配不同操作系统,强烈建议养成这个习惯。

4.5 本章知识体系

下面这张图帮你梳理本章的核心知识点,方便你快速回顾。

Python基础速通(下) 文件读写 open() 函数 with 语句自动关闭 读:read/readline/readlines 写:write/writelines 异常处理 try-except 捕获异常 多种异常分别处理 finally 释放资源 else 无异常时执行 面向对象编程 类(class)与对象 __init__ 构造方法 封装:私有属性 继承:复用代码 内置模块 datetime:时间处理 now/strftime/strptime os:文件与路径管理 getcwd/path.join/listdir 核心目标:能独立读写数据文件、处理运行时异常 用面向对象组织策略代码、熟练操作时间和文件系统

好了,这一章的内容就到这里。文件读写让你能跟数据文件打交道,异常处理让你的程序更健壮,面向对象帮你把代码组织得更清晰,datetime和os模块则是日常开发的好帮手。这些技能在后面的量化策略实战中会反复用到,建议你多写几遍代码,形成肌肉记忆。

记住,写代码不是背语法,而是解决问题。遇到不懂的,多查官方文档,多动手试。我在刚学Python时,就是靠一遍遍敲代码、一次次踩坑,才慢慢熟练起来的。你也一样,加油。


专注资料整理