五 量化交易全流程( 六 )


使用 搭建回测系统
在前面的讨论中 , 我们知道编写回测系统有三种方式 , 分别是向量化系统、For循环系统、事件驱动系统 。使用 语言 , 可以很方便地实现这三种系统 。
其中 , 向量化系统和For循环系统 , 其实自己从头开始写 , 也是可以的 , 并不算很复杂 , 而且具有充分的灵活性 。对于临时性的策略验证 , 我是比较推荐自己写脚本的 。
而对于事件驱动系统 , 不建议自己从头开始写 , 因为这会涉及整体的系统架构设计问题 , 这个领域其实是比较专业的IT人士才能胜任的 。如果只是投资经理或者研究员 , 那么花这个力气其实是没必要的 。
这里我们将依次讨论如何搭建这三种回测系统 , 使用的是比较简单的双均线突破策略 。策略的核心就是趋势跟踪 , 也就是说 , 当短期均线高于长期均线的时候 , 则认为是多头趋势 , 这个时候持多仓 。当短期均线低于长期均线的时候 , 则认为是空头趋势 , 这个时候持空仓 。
策略的具体逻辑如下所示 。
□ 计算两根移动均线 ma1、ma2 , 周期分别是len1、len2 , 其中len1 □当均线mal上穿ma2的时候 , 平掉空头仓位(如果有) , 买入做多 。
□当均线mal下穿ma2的时候 , 平掉多头仓位(如果有) , 卖出做空 。
这是一个很简单的策略 , 不太可能真的赚钱 。不过 , 这里只是为了演示如何编写回测程序 , 而不是试图介绍如何研究赚钱的策略 , 这是两码事 。
1、向量化回测
说是"系统" , 倒不如说是脚本 , 因为能使用向量化计算的策略 , 逻辑一般都比较简单 。这种策略使用的是向量化的计算 , 需要编写的代码并不多 。
不过市面上仍然有向量化的回测框架 , 这些框架一般会提供很多其他的功能 。我在这里将要讨论如何从头编写一个回测程序 。同时 , 也会简单介绍一些开源的向量化回测项目 。
首先 , 我们先用最容易理解的方式 , 即使用 的来实现这个双均线回测 。
要计算策略的表现 , 最重要的是算出每天的持仓情况 , 根据持仓情况再计算每天的盈亏 。从策略逻辑上 , 我们可以知道 , 只要 ma1 > ma2 , 那么就是持有多仓 , 只要mal假设我们有一个比较长的时间天数的股票数据 df , 保存在中 。前五行的数据如图所示:
import mysql.connectorimport pymysqlimport pandas as pdimport numpy as npclass StockData:def __init__(self):self.host = '127.0.0.1'self.user = 'root'self.password='152617'self.port= 3306 self.db='stock_info'def data_convert(self, df):df["open"] = df['open'].astype(float)df["high"] = df['high'].astype(float)df["low"] = df['low'].astype(float)df["close"] = df['close'].astype(float)df["volume"] = df['volume'].astype(float)df = df.dropna()return dfdef get_stock_data(self, stock_code, start_date, end_date):conn = pymysql.connect(host=self.host, user=self.user, password=self.password, port=self.port, db=self.db, charset='utf8')cur = conn.cursor()sql = f"select * from `stocks` where stock_code = {stock_code} and date > {start_date} and date < {end_date}"cur.execute(sql)data = http://www.kingceram.com/post/cur.fetchall()data = pd.DataFrame(data)data = data.rename(columns={0:"date", 1: "stock_code", 2: "open", 3: "high", 4: "low", 5: "close", 6: "volume"})df = self.data_convert(data)cur.close()conn.close()return dfsd = StockData()stock_code = '600519'start_date = '20230104'end_date = '20230904'df = sd.get_stock_data(stock_code, start_date, end_date)df.head()