跳到主要内容

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

· 5 分钟阅读
Eric Cheng
Eric Cheng
JAVA 後端工程師

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

  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