Python 交易回测工具 Backtesting.py 简易教学
计画要写所有「可能有用」的技术指标的回测文,但要完成这个目的,需要先有合适的工具,算是个简单然后没几篇文章的系列文来介绍这些工具吧
- 首先要能取得股价:请参考【如何使用Python取得历史股价,简介yfinance、ffn、FinMind】
- 要回测技术指标,当然要能产生取得技术指标:请参考【如何使用Python产生技术指标? TA-Lib简易教学】
然后有了股价、技术指标,再来就是回测工具了...
简介 Backtesting.py
最基本的,要使用 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 可以看到以下的资料
有在看这个 blog 回测文的,应该对大部份的指标都还算熟悉,这里就不一一解释每个名词了,比较有趣的是里面有个「Buy & Hold Return」,可以比较制订出来的策略和单纯的买入后持有之间的比较
以及可以直接绘图,用图形化更清楚的表示结果
置换资料源
看完 sample code 看似好像会有股价资料了,但仔细看 GOOG 的时间是 2004~2013,或者把 GOOG 换成 AAPL 就直接报错了,看了一下原始码,lib 只提供了 GOOG 和 EURUSD 的资料,所以资料源是需要自己找的
好在我们本来就知道怎么取得股价了,【如何使用Python取得历史股价,简介yfinance、ffn、FinMind】,现在实作把回测的股价换成台股的 0050
先看一下范例程式中 GOOG 的栏位
要用 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,没别的了
好在我们也本来就知道怎么取得技术指标,【如何使用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()
完整的回测结果就出来了
应该有人对这个结果有点熟悉,没错,这就是一年多前【K<20买,K>80卖真能打败大盘?用KD指标回测0050历史股价】这篇文章的程式码, 拖了一年多,终于把教学文件写出来了.. XD
小结
先这样吧,完整程式码我放在【Colab】,希望有帮到有兴趣的人