杰瑞科技汇

量化分析师Python日记第9天,学到了什么?

量化分析师的Python日记 第9天

日期: 2025年10月27日 天气: 晴,微风,像今天的心情一样,终于拨云见日。 心情: 🎉 兴奋、满足、如释重负


【今日主题】告别“魔法数字”,拥抱专业的回测框架

“小王,你上周用Pandas写的那个回测脚本,逻辑是对的,但代码结构有点‘面条化’(Spaghetti Code)了,手续费、滑点这些关键参数都硬编码在函数里,想换一个策略就得大改一遍,你画的那几张图,虽然能看,但不够专业,今天我教你用 Backtrader 这个专业框架,让你写的策略代码像搭积木一样清晰、可复用。”

今天是我的导师老张对我进行“代码升舱”指导的一天,他一针见血地指出了我之前纯用Pandas写回测的种种弊端,老实说,我也感觉到了,每次调整参数都心惊胆战,生怕改错一个地方就导致整个回测跑偏。

上午:理论武装——Backtrader的“三驾马车”

老张并没有直接让我上手敲代码,而是先给我讲了Backtrader的核心思想,他说,Backtrader的设计理念非常清晰,主要围绕三个核心概念:

  1. Cerebro (大脑): 这是整个回测系统的核心,它负责统筹一切,包括数据加载、策略实例化、初始设置(如资金、手续费)、运行回测以及最后的结果输出,你可以把它想象成量化交易的大脑中枢。

  2. Data (数据): 顾名思义,就是我们的交易数据,比如股票的OHLCV(开盘、最高、最低、收盘、成交量)数据,Backtrader对数据格式有严格要求,通常是包含日期和价格列的Pandas DataFrame或CSV文件。

  3. Strategy (策略): 这是我们策略逻辑的载体,所有具体的交易判断,比如什么时候买入、什么时候卖出、仓位多少,都写在一个继承自backtrader.Strategy的类中,这完美地实现了策略逻辑与回测引擎的解耦

老张画了一个简单的图来解释它们的关系:

[Data]  --->  [Cerebro]  --->  [Strategy]
   ^              |              |
   |              v              v
[加载的数据]  [运行回测]  [执行买卖逻辑]

这个“大脑-数据-策略”的三层架构,让我瞬间就明白了它的优势。策略代码变得非常纯粹,我只关心我的交易逻辑,而不用去关心数据如何加载、回测如何执行、图表如何绘制这些繁琐的后端工作,Cerebro会帮我搞定一切。

下午:实战演练——用Backtrader重构双均线策略

理论讲完了,该动手了,我决定用最经典的“双均线策略”来练手,对比一下和之前用Pandas写有什么天壤之别。

第一步:准备数据 我准备了一份苹果公司(AAPL)从2025年到2025年的日线数据,格式很简单:date, open, high, low, close, volume

第二步:构建策略类 这是最核心的一步,我需要创建一个继承自backtrader.Strategy的类,并实现两个关键方法:__init__next

  • __init__: 策略初始化时调用,我需要定义策略会用到的指标,比如5日和20日的移动平均线,Backtrader提供了非常方便的backtrader.indicators.SMA
  • next: 每一个交易日(Data Feed的每一行数据)都会调用一次,这里是策略决策的核心,我需要在这里编写判断逻辑:“如果短期均线上穿长期均线,则买入;如果短期均线下穿长期均线,则卖出。”
import backtrader as bt
import pandas as pd
import matplotlib.pyplot as plt
# 1. 定义策略类
class DualMovingAverageStrategy(bt.Strategy):
    params = (
        ('maperiod_short', 5),   # 短期均线周期
        ('maperiod_long', 20),    # 长期均线周期
    )
    def __init__(self):
        # 初始化指标
        self.sma_short = bt.indicators.SimpleMovingAverage(
            self.data.close, period=self.params.maperiod_short)
        self.sma_long = bt.indicators.SimpleMovingAverage(
            self.data.close, period=self.params.maperiod_long)
        # 为了方便画图,我们可以记录交叉点
        self.crossover = bt.indicators.CrossOver(self.sma_short, self.sma_long)
    def next(self):
        # 每个Bar都会执行这里
        if not self.position:  # 如果当前没有持仓
            if self.crossover > 0:  # 短期均线上穿长期均线
                self.buy()  # 买入
        else:  # 如果已经持仓
            if self.crossover < 0:  # 短期均线下穿长期均线
                self.close()  # 卖出
# 2. 设置Cerebro引擎
cerebro = bt.Cerebro()
# 3. 加载数据
# 假设数据文件名为 'AAPL.csv'
data = bt.feeds.YahooFinanceData(
    dataname='AAPL',
    fromdate=pd.to_datetime('2025-01-01'),
    todate=pd.to_datetime('2025-01-01')
)
cerebro.adddata(data)
# 4. 添加策略
cerebro.addstrategy(DualMovingAverageStrategy)
# 5. 设置初始资金
cerebro.broker.setcash(100000.0)
# 6. 添加分析器
# 添加交易分析器
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe_ratio')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trade_analyzer')
# 7. 运行回测
print(f'初始资金: {cerebro.broker.getvalue():.2f}')
initial_value = cerebro.broker.getvalue()
results = cerebro.run()
final_value = cerebro.broker.getvalue()
print(f'最终资金: {final_value:.2f}')
print(f'总收益: {final_value - initial_value:.2f}')
# 8. 打印分析结果
strat = results[0]
print(f'夏普比率: {strat.analyzers.sharpe_ratio.get_analysis()["sharperatio"]:.4f}')
print(f'最大回撤: {strat.analyzers.drawdown.get_analysis()["max"]["drawdown"]:.2f}%')
# 9. 绘制结果
# 设置绘图风格
plt.style.use('seaborn-v0_8-darkgrid')
cerebro.plot(style='candlestick', barup='red', bardown='green', volume=True)
plt.show()

下午的成果与反思:

仅仅用了不到100行代码(其中大部分是注释和打印),我就完成了一个结构清晰、功能完善的回测系统。

  • 代码之美: 代码逻辑一目了然。DualMovingAverageStrategy类只关心交易本身,Cerebro负责调度,如果我想测试不同均线周期,只需要在addstrategy时传入参数即可,完全不需要修改策略类的代码!
  • 功能之强: Backtrader内置了大量的分析器(Analyzer),如夏普比率、最大回撤、交易统计等,一键添加,省去了我手动计算的麻烦,绘制的图表也专业得多,K线、交易点、指标线一应俱全。
  • 心态之变: 以前写回测,总感觉像在“黑箱”里操作,担心数据加载错误、逻辑判断有疏漏,有了框架的保障,我更专注于策略本身的思考,这让我对未来的研究充满了信心。

今日总结:

今天是我量化编程道路上一个重要的里程碑,从“野蛮生长”的Pandas脚本,到“秩序井然”的Backtrader框架,我不仅学到了一个新工具,更重要的是,建立了一种“工程化”的思维,量化交易不仅仅是找到一个能赚钱的策略,更是要能清晰、高效、可复现地去验证和迭代这个策略。

明天,我打算尝试在策略中加入手续费和滑点的设置,看看这个框架的灵活性如何,今天的“升舱”非常成功,感觉自己的代码“专业度”又上了一个新台阶!晚安,我的策略们。

分享:
扫描分享到社交APP
上一篇
下一篇