问题描述
解决方案
这是一个基于**威廉鳄鱼线(Williams Alligator)**指标的量化策略实现。
策略原理
鳄鱼线指标由比尔·威廉姆斯(Bill Williams)发明,旨在通过三条平滑移动平均线(SMMA)的组合来识别趋势的开始和结束。
- 鳄鱼的下巴 (Jaw - 蓝线): 13周期SMMA(平滑移动平均),向未来平移8个周期。
- 鳄鱼的牙齿 (Teeth - 红线): 8周期SMMA,向未来平移5个周期。
- 鳄鱼的嘴唇 (Lips - 绿线): 5周期SMMA,向未来平移3个周期。
交易逻辑:
- 多头信号(张嘴进食): 嘴唇 > 牙齿 > 下巴 (绿 > 红 > 蓝),且价格收盘价高于嘴唇。这表示趋势向上。
- 平仓/空头信号(闭嘴睡觉): 价格跌破牙齿(红线)或下巴(蓝线),或者均线缠绕(不再满足多头排列)。本策略简化为:当价格跌破下巴(蓝线)或均线不再呈多头排列时平仓。
策略代码
# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from jqdata import *
def initialize(context):
"""
初始化函数,设定基准、手续费、滑点等
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开开启真实价格模式(动态复权)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定手续费:股票买入万分之三,卖出万分之三加千分之一印花税
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# 设定要操作的股票池(这里示例使用沪深300成分股的前10只,避免回测过慢)
g.security_pool = get_index_stocks('000300.XSHG')[:10]
# 鳄鱼线参数
g.N1, g.shift1 = 13, 8 # 下巴 (Jaw)
g.N2, g.shift2 = 8, 5 # 牙齿 (Teeth)
g.N3, g.shift3 = 5, 3 # 嘴唇 (Lips)
# 每天开盘时运行策略
run_daily(trade_func, time='open')
def get_smma(series, n):
"""
计算平滑移动平均线 (SMMA)
SMMA 等同于 alpha = 1/n 的指数移动平均 (EMA)
"""
return series.ewm(alpha=1.0/n, adjust=False).mean()
def trade_func(context):
"""
每日交易主逻辑
"""
# 获取足够长的历史数据以计算指标 (取100天以保证MA计算稳定)
# 注意:鳄鱼线需要向未来平移,所以我们需要回溯 (Max_N + Max_Shift) 以上的数据
# 这里取100天足够覆盖
hist_len = 100
for stock in g.security_pool:
# 获取历史数据:高、低、收
data = attribute_history(stock, hist_len, '1d', ['high', 'low', 'close'], skip_paused=True)
if len(data) < 30:
continue
# 1. 计算中价 (Median Price) = (High + Low) / 2
median_price = (data['high'] + data['low']) / 2
# 2. 计算三条SMMA轨道
# 注意:这里计算的是基于历史数据的序列
smma_jaw = get_smma(median_price, g.N1) # 13周期
smma_teeth = get_smma(median_price, g.N2) # 8周期
smma_lips = get_smma(median_price, g.N3) # 5周期
# 3. 获取"当前"生效的指标值 (考虑平移)
# 这里的逻辑是:今天的指标值 = (Shift)天前计算出来的SMMA值
# data的最后一个数据索引是 -1 (昨天收盘),我们需要基于昨天的数据判断今天开盘的操作
# 下巴 (Jaw): 13周期,平移8 --> 取倒数第 (1 + 8) 个数据
jaw_value = smma_jaw.iloc[-(1 + g.shift1)]
# 牙齿 (Teeth): 8周期,平移5 --> 取倒数第 (1 + 5) 个数据
teeth_value = smma_teeth.iloc[-(1 + g.shift2)]
# 嘴唇 (Lips): 5周期,平移3 --> 取倒数第 (1 + 3) 个数据
lips_value = smma_lips.iloc[-(1 + g.shift3)]
# 获取昨天的收盘价
current_close = data['close'].iloc[-1]
# 获取当前持仓
position = context.portfolio.positions[stock]
# --- 交易信号判断 ---
# 多头排列条件:嘴唇 > 牙齿 > 下巴 (绿 > 红 > 蓝)
is_long_trend = (lips_value > teeth_value) and (teeth_value > jaw_value)
# 价格确认:收盘价在嘴唇之上 (表示动能强劲)
price_confirm = current_close > lips_value
# 买入逻辑
if is_long_trend and price_confirm:
if position.total_amount == 0:
# 全仓买入 (这里简单均分资金,实际可调整仓位管理)
cash_per_stock = context.portfolio.available_cash / (len(g.security_pool) / 2) # 假设同时持有一半的股票
order_value(stock, cash_per_stock)
log.info(f"买入 {stock}: 鳄鱼张嘴,趋势向上")
# 卖出逻辑
# 如果价格跌破下巴 (蓝线),或者均线不再呈多头排列,则平仓
elif (current_close < jaw_value) or (not is_long_trend):
if position.closeable_amount > 0:
order_target(stock, 0)
log.info(f"卖出 {stock}: 鳄鱼闭嘴或价格破位")
代码关键点解析
-
SMMA 计算:
- 标准的鳄鱼线使用的是 SMMA(Smoothed Moving Average)。在 Pandas 中,SMMA 可以通过
ewm(alpha=1/n)来精确模拟。 - 代码中定义了
get_smma辅助函数来处理这个计算。
- 标准的鳄鱼线使用的是 SMMA(Smoothed Moving Average)。在 Pandas 中,SMMA 可以通过
-
平移 (Shift) 的处理:
- 这是鳄鱼线最容易出错的地方。指标定义是“向未来平移”。
- 在回测或实盘中,这意味着:今天看到的“下巴”值,实际上是8天前计算出来的SMMA数值。
- 代码中使用
iloc[-(1 + g.shift)]来获取正确的值。例如,shift=8,我们要取的是倒数第9个数据(因为倒数第1个是昨天,倒数第9个是8天前)。
-
中价 (Median Price):
- 鳄鱼线不使用收盘价计算均线,而是使用
(最高价 + 最低价) / 2,代码中正确实现了这一点。
- 鳄鱼线不使用收盘价计算均线,而是使用
-
买卖逻辑:
- 买入: 严格遵循
Lips > Teeth > Jaw且Close > Lips,确保是在趋势爆发期介入。 - 卖出: 当价格跌穿最下方的支撑线(下巴/蓝线)或者均线纠缠(趋势结束)时离场。
- 买入: 严格遵循
如何使用
- 登录聚宽官网 (JoinQuant)。
- 进入“我的策略” -> “新建策略”。
- 将上述代码复制粘贴到代码编辑区。
- 点击“编译运行”进行回测。
常见问题 (Q&A)
Q: 为什么使用 SMMA 而不是普通的 SMA 或 EMA?
A: 比尔·威廉姆斯在设计该指标时特意选择了平滑移动平均(SMMA),因为它比 SMA 更平滑,比 EMA 对旧数据的遗忘速度更慢,适合捕捉中长期趋势并过滤震荡噪音。
Q: 策略中的 shift 是如何实现的?
A: 在绘图中,shift 是将线条向右画。在量化逻辑中,这意味着当前的指标值等于 $N$ 天前的计算值。代码通过 iloc 索引回溯历史数据来实现这一逻辑,避免了未来函数。
Q: 这个策略适合什么行情?
A: 鳄鱼线是典型的趋势跟踪策略。它在单边大牛市或大熊市中表现优异(鳄鱼张嘴进食),但在震荡市(鳄鱼睡觉)中可能会频繁止损。建议结合其他过滤器(如分形指标 Fractals)使用以提高胜率。