PyBroker量化交易系列:第二部分 策略开发
本教程全面指导读者掌握PyBroker框架在量化交易中的应用。涵盖安装配置、开发环境搭建与数据处理技巧。通过构建和优化简单交易策略,逐步深入多因子模型设计与实现。深入讲解风险管理、资金管理、回测分析、持续监控和调整策略等关键环节,助力读者掌握稳健交易策略开发全流程。
文中内容仅限技术学习与代码实践参考,市场存在不确定性,技术分析需谨慎验证,不构成任何投资建议。适合量化新手建立系统认知,为策略开发打下基础。
通过一个简单的例子(例如移动平均线交叉策略),学习如何定义买卖信号。
移动平均线交叉策略是一种常见的技术分析策略,通过两条不同周期的移动平均线的交叉来生成买卖信号。具体来说,当短期移动平均线上穿长期移动平均线时,发出买入信号;当短期移动平均线下穿长期移动平均线时,发出卖出信号。
下面是一个简单的移动平均线交叉策略的例子:
import pybroker
from pybroker import Strategy, StrategyConfig, indicator
import talib
# 启用数据源缓存
pybroker.enable_data_source_cache("my_strategy")
# 启用指标缓存
pybroker.enable_indicator_cache("my_indicators")
# 初始化 Tushare 数据源
data_source = TushareDataSource()
# 自定义指标:快线(5日均线)和慢线(20日均线)
fast_ma = indicator("fast_ma", lambda data: talib.MA(data.close, timeperiod=5))
slow_ma = indicator("slow_ma", lambda data: talib.MA(data.close, timeperiod=20))
# 定义交易策略
def exec_fn(ctx):
# 获取当前股票的均线数据
fast_ma = ctx.indicator("fast_ma")
slow_ma = ctx.indicator("slow_ma")
# 无持仓且快线上穿慢线时买入
if not ctx.long_pos() and fast_ma[-1] > slow_ma[-1] and fast_ma[-2] <= slow_ma[-2]:
ctx.buy_shares = 100 # 买入100股
# 持仓且快线下穿慢线时卖出
elif ctx.long_pos() and fast_ma[-1] < slow_ma[-1] and fast_ma[-2] >= slow_ma[-2]:
ctx.sell_all_shares() # 平仓
# 初始化策略配置
config = StrategyConfig(
initial_cash=100000, fee_amount=0.0005 # 初始资金10万元 # 交易费率万五
)
# 创建策略实例
strategy = Strategy(
data_source, # 使用自定义数据源
start_date="20240101",
end_date="20241231",
config=config,
)
# 示例股票代码
# 贵州茅台 (600519.SH)
# 五粮液 (000858.SZ)
symbols = ["600519.SH", "000858.SZ"]
# 添加执行规则和股票代码
strategy.add_execution(
exec_fn,
symbols=symbols,
indicators=[fast_ma, slow_ma],
)
实现策略后,我们需要运行回测来验证策略的表现。以下是如何使用 PyBroker 进行回测的示例:
# 运行回测(预热20天以确保均线计算)
result = strategy.backtest(warmup=20)
# 输出回测结果
print("\n回测结果:")
print(result.metrics_df)
print("\n订单记录:")
print(result.orders)
在上述代码中,我们首先定义了策略的执行函数 exec_fn
,该函数根据移动平均线交叉来生成买卖信号。然后,我们配置了策略参数,并创建了一个策略实例。最后,我们调用 strategy.backtest()
方法来运行回测,并打印出回测结果和订单记录。
参数优化是量化交易策略开发中的一个重要环节,旨在找到一组最优参数组合,以提高策略的表现。常用的方法包括网格搜索和遗传算法。下面详细介绍这两种方法及其在PyBroker中的应用。
网格搜索是一种暴力搜索方法,通过遍历所有可能的参数组合来找到最优参数。这种方法简单直观,但计算成本较高,尤其是在参数数量较多的情况下。
假设我们有一个简单的移动平均线交叉策略,需要优化两个参数:短期移动平均线的周期 short_period
和长期移动平均线的周期 long_period
。我们可以使用网格搜索来找到这两个参数的最佳组合。
from pybroker import Strategy, StrategyConfig, indicator
import talib
from itertools import product
# 定义自定义数据源(假设已经定义好)
data_source = TushareDataSource()
# 定义策略执行函数
def exec_fn(ctx, short_period, long_period):
# 自定义指标:快线(短期均线)和慢线(长期均线)
fast_ma = indicator(
"fast_ma", lambda data: talib.MA(data.close, timeperiod=short_period)
)
slow_ma = indicator(
"slow_ma", lambda data: talib.MA(data.close, timeperiod=long_period)
)
# 获取当前股票的均线数据
fast_ma = ctx.indicator("fast_ma")
slow_ma = ctx.indicator("slow_ma")
# 无持仓且快线上穿慢线时买入
if not ctx.long_pos() and fast_ma[-1] > slow_ma[-1] and fast_ma[-2] <= slow_ma[-2]:
ctx.buy_shares = 100 # 买入100股
# 持仓且快线下穿慢线时卖出
elif ctx.long_pos() and fast_ma[-1] < slow_ma[-1] and fast_ma[-2] >= slow_ma[-2]:
ctx.sell_all_shares() # 平仓
# 初始化策略配置
config = StrategyConfig(
initial_cash=100000, fee_amount=0.0005 # 初始资金10万元 # 交易费率万五
)
# 定义参数范围
short_periods = [5, 10, 15, 20]
long_periods = [20, 25, 30, 35, 40]
# 初始化最佳参数和最佳绩效
best_params = None
best_performance = float("-inf")
# 网格搜索
for short_period, long_period in product(short_periods, long_periods):
# 创建策略实例
strategy = Strategy(
data_source, # 使用自定义数据源
start_date="20240101",
end_date="20241231",
config=config,
)
# 示例股票代码
symbols = ["600519.SH", "000858.SZ"]
# 添加执行规则和股票代码
strategy.add_execution(
lambda ctx: exec_fn(ctx, short_period, long_period),
symbols=symbols,
indicators=[
indicator(
"fast_ma", lambda data: talib.MA(data.close, timeperiod=short_period)
),
indicator(
"slow_ma", lambda data: talib.MA(data.close, timeperiod=long_period)
),
],
)
# 运行回测(预热20天以确保均线计算)
result = strategy.backtest(warmup=20)
# 获取夏普比率作为绩效指标
sharpe_ratio = result.metrics.sharpe
# 更新最佳参数和最佳绩效
if sharpe_ratio > best_performance:
best_performance = sharpe_ratio
best_params = (short_period, long_period)
print(
f"Short Period: {short_period}, Long Period: {long_period}, Sharpe Ratio: {sharpe_ratio}"
)
print(
f"\nBest Parameters: Short Period: {best_params[0]}, Long Period: {best_params[1]}, Best Sharpe Ratio: {best_performance}"
)
遗传算法是一种启发式搜索算法,模仿自然选择的过程,通过选择、交叉和变异等操作来优化参数。这种方法适用于复杂的优化问题,能够有效避免局部最优解。
我们可以使用 DEAP
库来实现遗传算法。以下是一个示例,展示如何使用遗传算法优化移动平均线交叉策略的参数。
首先,安装 DEAP
库:
poetry add deap
然后,实现遗传算法:
from pybroker import Strategy, StrategyConfig, indicator
import talib
import random
from deap import base, creator, tools, algorithms
# 定义自定义数据源(假设已经定义好)
data_source = TushareDataSource()
# 定义策略执行函数
def exec_fn(ctx, short_period, long_period):
# 自定义指标:快线(短期均线)和慢线(长期均线)
fast_ma = indicator(
"fast_ma", lambda data: talib.MA(data.close, timeperiod=short_period)
)
slow_ma = indicator(
"slow_ma", lambda data: talib.MA(data.close, timeperiod=long_period)
)
# 获取当前股票的均线数据
fast_ma = ctx.indicator("fast_ma")
slow_ma = ctx.indicator("slow_ma")
# 无持仓且快线上穿慢线时买入
if not ctx.long_pos() and fast_ma[-1] > slow_ma[-1] and fast_ma[-2] <= slow_ma[-2]:
ctx.buy_shares = 100 # 买入100股
# 持仓且快线下穿慢线时卖出
elif ctx.long_pos() and fast_ma[-1] < slow_ma[-1] and fast_ma[-2] >= slow_ma[-2]:
ctx.sell_all_shares() # 平仓
# 初始化策略配置
config = StrategyConfig(
initial_cash=100000, fee_amount=0.0005 # 初始资金10万元 # 交易费率万五
)
# 定义适应度函数
def evaluate(individual):
short_period, long_period = individual
# 创建策略实例
strategy = Strategy(
data_source, # 使用自定义数据源
start_date="20240101",
end_date="20241231",
config=config,
)
# 示例股票代码
symbols = ["600519.SH", "000858.SZ"]
# 添加执行规则和股票代码
strategy.add_execution(
lambda ctx: exec_fn(ctx, short_period, long_period),
symbols=symbols,
indicators=[
indicator(
"fast_ma", lambda data: talib.MA(data.close, timeperiod=short_period)
),
indicator(
"slow_ma", lambda data: talib.MA(data.close, timeperiod=long_period)
),
],
)
# 运行回测(预热20天以确保均线计算)
result = strategy.backtest(warmup=20)
# 获取夏普比率作为适应度
sharpe_ratio = result.metrics.sharpe
return (sharpe_ratio,)
# 设置遗传算法参数
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)
toolbox = base.Toolbox()
toolbox.register("attr_short_period", random.randint, 5, 20)
toolbox.register("attr_long_period", random.randint, 20, 40)
toolbox.register(
"individual",
tools.initCycle,
creator.Individual,
(toolbox.attr_short_period, toolbox.attr_long_period),
n=1,
)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("evaluate", evaluate)
toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mutate", tools.mutUniformInt, low=[5, 20], up=[20, 40], indpb=0.2)
toolbox.register("select", tools.selTournament, tournsize=3)
# 创建初始种群
population = toolbox.population(n=50)
# 运行遗传算法
ngen = 40
cxpb = 0.5
mutpb = 0.2
for gen in range(ngen):
offspring = algorithms.varAnd(population, toolbox, cxpb, mutpb)
fits = toolbox.map(toolbox.evaluate, offspring)
for fit, ind in zip(fits, offspring):
ind.fitness.values = fit
population = toolbox.select(offspring, k=len(population))
# 获取最优个体
best_individual = tools.selBest(population, k=1)[0]
best_short_period, best_long_period = best_individual
best_sharpe_ratio = evaluate(best_individual)[0]
print(
f"\nBest Parameters: Short Period: {best_short_period}, Long Period: {best_long_period}, Best Sharpe Ratio: {best_sharpe_ratio}"
)
通过参数优化,我们可以找到一组最优参数组合。但是,了解不同参数设置对策略表现的影响也是非常重要的。以下是一些分析方法:
绘制参数敏感性图表:
统计分析:
可视化结果:
import matplotlib.pyplot as plt
import numpy as np
# 定义参数范围
short_periods = np.arange(5, 21)
long_periods = np.arange(20, 41)
# 初始化性能矩阵
performance_matrix = np.zeros((len(short_periods), len(long_periods)))
# 计算每个参数组合的性能
for i, short_period in enumerate(short_periods):
for j, long_period in enumerate(long_periods):
# 创建策略实例
strategy = Strategy(
data_source, # 使用自定义数据源
start_date="20240101",
end_date="20241231",
config=config,
)
# 示例股票代码
symbols = ["600519.SH", "000858.SZ"]
# 添加执行规则和股票代码
strategy.add_execution(
lambda ctx: exec_fn(ctx, short_period, long_period),
symbols=symbols,
indicators=[
indicator(
"fast_ma",
lambda data: talib.MA(data.close, timeperiod=short_period),
),
indicator(
"slow_ma", lambda data: talib.MA(data.close, timeperiod=long_period)
),
],
)
# 运行回测(预热20天以确保均线计算)
result = strategy.backtest(warmup=20)
# 获取夏普比率作为性能指标
sharpe_ratio = result.metrics.sharpe
performance_matrix[i, j] = sharpe_ratio
绘制热力图
plt.figure(figsize=(10, 8))
plt.imshow(
performance_matrix,
cmap="hot",
interpolation="nearest",
extent=[20, 40, 5, 20],
origin="lower",
)
plt.colorbar(label="Sharpe Ratio")
plt.xlabel("Long Period")
plt.ylabel("Short Period")
plt.title("Parameter Sensitivity Analysis")
plt.show()
通过上述方法,我们可以有效地进行参数优化,并分析不同参数设置对策略表现的影响。这有助于提高策略的稳定性和盈利能力。
构建多因子模型是量化交易中的一种高级策略,通过结合多个技术指标和技术面因素来提高策略的准确性和鲁棒性。以下是详细的步骤和示例代码,帮助你理解和实现多因子模型。
多因子模型通常涉及多个技术指标,如移动平均线、相对强弱指数(RSI)、布林带等。通过结合这些指标,可以更全面地评估市场状况并生成更可靠的买卖信号。
首先,我们需要定义多个技术指标。以下是一些常见的技术指标及其定义:
import os
from pybroker.data import DataSource
import pandas as pd
import talib
class TushareDataSource(DataSource):
def __init__(self):
super().__init__()
# 注册技术指标字段列
pybroker.register_columns(
"fast_ma",
"slow_ma",
"rsi",
"macd_line",
"signal_line",
"macd_histogram",
"upper_band",
"middle_band",
"lower_band",
)
def _fetch_data(self, symbols, start_date, end_date, _timeframe, _adjust):
"""
获取指定股票代码、起始日期和结束日期的历史数据。
:param symbols: 股票代码列表
:param start_date: 起始日期 (格式: YYYY-MM-DD)
:param end_date: 结束日期 (格式: YYYY-MM-DD)
:return: 包含历史数据的 Pandas DataFrame
"""
data_frames = []
for symbol in symbols:
file_path = f"./data/processed_historical_data_{symbol}.parquet"
if os.path.exists(file_path):
df = pd.read_parquet(file_path)
df = df[
(df["trade_date"] >= start_date) & (df["trade_date"] <= end_date)
]
data_frames.append(df)
if not data_frames:
return pd.DataFrame(
columns=["symbol", "date", "open", "high", "low", "close"]
)
df = pd.concat(data_frames, ignore_index=True)
df.rename(
columns={
"trade_date": "date",
"ts_code": "symbol",
# "open": "open",
# "high": "high",
# "low": "low",
# "close": "close",
"vol": "volume",
},
inplace=True,
)
# 计算技术指标
df["fast_ma"] = talib.MA(df["close"], timeperiod=5)
df["slow_ma"] = talib.MA(df["close"], timeperiod=20)
df["rsi"] = talib.RSI(df["close"], timeperiod=14)
df["macd_line"], df["signal_line"], df["macd_histogram"] = talib.MACD(
df["close"], fastperiod=12, slowperiod=26, signalperiod=9
)
df["upper_band"], df["middle_band"], df["lower_band"] = talib.BBANDS(
df["close"], timeperiod=20, nbdevup=2, nbdevdn=2, matype=0
)
return df
接下来,我们需要定义策略的执行函数,根据多个技术指标生成买卖信号。以下是一个示例:
def exec_fn(ctx):
# 获取当前股票的技术指标数据
fast_ma_val = ctx.fast_ma
slow_ma_val = ctx.slow_ma
rsi_val = ctx.rsi
macd_val = ctx.macd_line
macdsignal_val = ctx.signal_line
macdhist_val = ctx.macd_histogram
upper_band_val = ctx.upper_band
middle_band_val = ctx.middle_band
lower_band_val = ctx.lower_band
# 无持仓且满足多个条件时买入
if not ctx.long_pos():
# if (
# fast_ma_val[-1] > slow_ma_val[-1] and fast_ma_val[-2] <= slow_ma_val[-2]
# ): # 快线上穿慢线
if rsi_val[-1] < 30: # RSI低于超卖区
# if (
# macd_val[-1] > macdsignal_val[-1] and macd_val[-2] <= macdsignal_val[-2]
# ): # MACD线上穿信号线
if ctx.close[-1] < lower_band_val[-1]: # 价格低于布林带下轨
ctx.buy_shares = 100 # 买入100股
# 持仓且满足多个条件时卖出
elif ctx.long_pos():
# if (
# fast_ma_val[-1] < slow_ma_val[-1] and fast_ma_val[-2] >= slow_ma_val[-2]
# ): # 快线下穿慢线
if rsi_val[-1] > 70: # RSI高于超买区
# if (
# macd_val[-1] < macdsignal_val[-1] and macd_val[-2] >= macdsignal_val[-2]
# ): # MACD线下穿信号线
if ctx.close[-1] > upper_band_val[-1]: # 价格高于布林带上轨
ctx.sell_all_shares() # 平仓
最后,我们创建策略实例并运行回测:
# 初始化策略配置
config = StrategyConfig(
initial_cash=100000, fee_amount=0.0005 # 初始资金10万元 # 交易费率万五
)
# 创建策略实例
strategy = Strategy(
data_source, # 使用自定义数据源
start_date="20240101",
end_date="20241231",
config=config,
)
# 示例股票代码
symbols = ["600519.SH", "000858.SZ"]
# 添加执行规则和股票代码
strategy.add_execution(exec_fn, symbols=symbols)
# 运行回测(预热20天以确保指标计算)
result = strategy.backtest(warmup=20)
# 输出回测结果
print("\n回测结果:")
print(result.metrics_df)
print("\n订单记录:")
print(result.orders)
结合不同的技术分析工具和技术面因素可以提高策略的灵活性和准确性。以下是一些常见的技术分析工具和技术面因素:
移动平均线 MA
移动平均线(Moving Average, MA)是衡量一段时间内价格平均值的趋势指标。常见的移动平均线包括简单移动平均线(SMA)和指数移动平均线(EMA)。
相对强弱指数 RSI
相对强弱指数(Relative Strength Index, RSI)用于衡量市场的超买和超卖情况。RSI的取值范围是0到100,通常认为RSI小于30为超卖区,RSI大于70为超买区。
移动平均收敛/发散指标 MACD
移动平均收敛/发散指标(Moving Average Convergence Divergence, MACD)用于识别趋势的方向和力度。MACD线和信号线的交叉可以生成买卖信号。
布林带 BBands
布林带(Bollinger Bands)由一条移动平均线和两条标准差线组成,用于衡量价格的波动性。价格突破布林带上轨或下轨可以生成买卖信号。
通过结合多个技术指标和技术分析工具,我们可以构建一个更为复杂和稳健的交易策略。这有助于提高策略的准确性和适应性,从而在不同的市场条件下获得更好的表现。
风险提示与免责声明
本文内容基于公开信息研究整理,不构成任何形式的投资建议。历史表现不应作为未来收益保证,市场存在不可预见的波动风险。投资者需结合自身财务状况及风险承受能力独立决策,并自行承担交易结果。作者及发布方不对任何依据本文操作导致的损失承担法律责任。市场有风险,投资须谨慎。