跳到主要内容

Python 交易回测工具 Backtesting.py 简易教学

· 7 分钟阅读
Eric Cheng

计画要写所有「可能有用」的技术指标的回测文,但要完成这个目的,需要先有合适的工具,算是个简单然后没几篇文章的系列文来介绍这些工具吧

  1. 首先要能取得股价:请参考【如何使用Python取得历史股价,简介yfinance、ffn、FinMind
  2. 要回测技术指标,当然要能产生取得技术指标:请参考【如何使用Python产生技术指标? TA-Lib简易教学

然后有了股价、技术指标,再来就是回测工具了...

简介 Backtesting.py

官网】、【Github

最基本的,要使用 library,要先执行安装

pip3 install backtesting

官网 sample code 说明

官网上直接有 sample code,那当然就是先直接跑一次看看结果,这篇文章撰写时的 sample code 如下

from backtesting import Backtest, Strategy
from backtesting.lib import crossover

from backtesting.test import SMA, GOOG


class SmaCross(Strategy):
n1 = 10
n2 = 20

def init(self):
close = self.data.Close
self.sma1 = self.I(SMA, close, self.n1)
self.sma2 = self.I(SMA, close, self.n2)

def next(self):
if crossover(self.sma1, self.sma2):
self.buy()
elif crossover(self.sma2, self.sma1):
self.sell()


bt = Backtest(GOOG, SmaCross,
cash=10000, commission=.002,
exclusive_orders=True)

output = bt.run()
bt.plot()

很简单的范例,说明一下这个范例做了哪些事

  • 从 backtesting.test 取得 GOOG 的股价
  • 从 backtesting.test 使用 SMA(移动平均) function 取得 10 日和 20 日的移动平均值
  • 从 backtesting.lib 使用 crossover function 执行策略:
    当 10 日移动平均往上穿越 20 日移动平均时「买进」
    当 10 日移动平均往下穿越 20 日移动平均时「卖出」
  • 范例的三个参数:
    cash: 初始的现金
    commission: 交易成本,就交易一次的手续费、交易税的总合
    exclusive_orders: True 代表每笔新交易会关闭上一次的交易

print output 可以看到以下的资料

backtesting.py 范例程式结果 1

有在看这个 blog 回测文的,应该对大部份的指标都还算熟悉,这里就不一一解释每个名词了,比较有趣的是里面有个「Buy & Hold Return」,可以比较制订出来的策略和单纯的买入后持有之间的比较

以及可以直接绘图,用图形化更清楚的表示结果

backtesting.py 范例程式结果 2

置换资料源

看完 sample code 看似好像会有股价资料了,但仔细看 GOOG 的时间是 2004~2013,或者把 GOOG 换成 AAPL 就直接报错了,看了一下原始码,lib 只提供了 GOOG 和 EURUSD 的资料,所以资料源是需要自己找的

backtesting.py 测试资料

好在我们本来就知道怎么取得股价了,【如何使用Python取得历史股价,简介yfinance、ffn、FinMind】,现在实作把回测的股价换成台股的 0050

先看一下范例程式中 GOOG 的栏位

backtesting.py 测试资料栏位

要用 backtesting.py 资料的栏位要一样,要不然会出现data must be a pandas.DataFrame with columns 'Open', 'High', 'Low', 'Close', and (optionally) 'Volume'的错误讯息

FinMind 的资料就要做些转换,像是将栏位第一个字母改成大写 ex: open => Open,以及将 index 改为日期的型态,完整程式如下

from FinMind.data import DataLoader
import pandas as pd

dl = DataLoader()
df = dl.taiwan_stock_daily(stock_id='0050', start_date='2003-01-01', end_date='2023-02-25')

df = df.rename(columns={"date": "Date"})
df.set_index("Date" , inplace=True)
df = df.rename(columns={"open": "Open", "max": "High", "min": "Low", "close": "Close", "Trading_Volume": "Volume"})
df = df.rename(columns={"Trading_Volume": "Volume"})
df = df.set_index(pd.DatetimeIndex(pd.to_datetime(df.index)))

置换技术指标

sample code 中有 SMA(移动平均),好像也有技术指标了?实际上看一下 Github 原始码,可以看到它其实就只有一个 SMA functoin,没别的了

backtesting.py SMA function

好在我们也本来就知道怎么取得技术指标,【如何使用Python产生技术指标? TA-Lib简易教学】,现在实作日K<20买进,日K>80卖出

完整程式如下

from backtesting import Backtest, Strategy
from backtesting.lib import crossover

from FinMind.data import DataLoader
import pandas as pd

import talib
from talib import abstract

## 取得资料
dl = DataLoader()
df = dl.taiwan_stock_daily(stock_id='0050', start_date='2003-01-01', end_date='2023-02-25')
## 整理资料格式
df = df.rename(columns={"date": "Date"})
df.set_index("Date" , inplace=True)
df = df.set_index(pd.DatetimeIndex(pd.to_datetime(df.index)))
## backtesting.py 格式
df1 = df.rename(columns={"open": "Open", "max": "High", "min": "Low", "close": "Close", "Trading_Volume": "Volume"})
## ta-lib 格式
df2 = df.rename(columns={"max": "high", "min": "low", "Trading_Volume": "Volume"})
## 取得 KD 值
df_kd = abstract.STOCH(df2,fastk_period=9, slowk_period=3,slowd_period=3)
## 合并资料
df1 = pd.merge(df1, df_kd, on="Date")

## KD 策略
class KdCross(Strategy):
def init(self):
super().init()

def next(self):
if crossover(20, self.data.slowk): ## K<20 买进
self.buy()
elif crossover(self.data.slowk, 80): ## K>80 平仓
self.position.close()

bt = Backtest(df1, KdCross, cash=10000, commission=.001798) ## 交易成本 0.1798%
output = bt.run()
bt.plot()

完整的回测结果就出来了 backtesting.py 0050 KD

应该有人对这个结果有点熟悉,没错,这就是一年多前【K<20买,K>80卖真能打败大盘?用KD指标回测0050历史股价】这篇文章的程式码, 拖了一年多,终于把教学文件写出来了.. XD

小结

先这样吧,完整程式码我放在【Colab】,希望有帮到有兴趣的人

版权声明

,转载请注明出处
本文键接: https://havocfuture.tw/zh-hans/blog/python-backtesting-py