如何设置在首次出现金叉时买入?
设置一个策略,每天循环,金叉时买入。比如金叉第一天时买入了,但第二第三天第四天还处于金叉阶段并符合买入条件,怎么设置只在首次出现金叉时买入,其余时间不买入呢?
问题描述
解决方案
在量化交易中,“金叉”通常指的是短期均线上穿长期均线。要实现**“只在首次出现金叉时买入,而在金叉持续期间不重复买入”,核心在于将“金叉”从一个持续的状态转化为一个瞬间的事件**。
核心逻辑
要判断“首次金叉”,我们需要同时对比昨天和今天的均线状态:
- 昨天:短期均线 $\le$ 长期均线(还没金叉,或者刚好粘合)
- 今天:短期均线 $>$ 长期均线(已经金叉)
只有同时满足这两个条件,才说明今天是“首次”发生金叉。如果昨天短均线已经大于长均线,今天依然大于,说明处于金叉的延续阶段,不触发买入。
此外,为了更加严谨,我们还可以结合账户持仓状态进行双重过滤:如果当前已经持有该股票,则不再买入。
JoinQuant 策略源码示例
以下是一个完整的 JoinQuant 策略代码,以 5日均线(MA5)上穿 10日均线(MA10)为例:
# 导入聚宽函数库
import jqdata
# 初始化函数
def initialize(context):
# 设定要操作的股票:平安银行
g.security = '000001.XSHE'
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 每天开盘时运行
run_daily(market_open, time='09:30')
def market_open(context):
security = g.security
# 为了计算今天和昨天的 MA10,我们至少需要过去 11 天的收盘价数据
# 获取过去 11 天的收盘价
close_data = attribute_history(security, 11, '1d', ['close'])
if len(close_data) < 11:
return # 数据不足,跳过
close_prices = close_data['close']
# 计算今天的 MA5 和 MA10 (包含昨天及之前的4天/9天,加上今天开盘前能获取到的最新价,即昨收)
# 注意:在每天 09:30 运行 attribute_history 获取的日线数据,最后一条是昨天的收盘价
# 所以这里的“今天”实际上是基于昨天收盘后的数据计算的均线,用于今天开盘的决策
ma5_today = close_prices[-5:].mean()
ma10_today = close_prices[-10:].mean()
# 计算昨天的 MA5 和 MA10 (排除最后一天的数据)
ma5_yesterday = close_prices[-6:-1].mean()
ma10_yesterday = close_prices[-11:-1].mean()
# 获取当前资金和持仓
cash = context.portfolio.available_cash
current_position = context.portfolio.positions[security].closeable_amount
# 核心判断:首次金叉事件
# 条件1:昨天 MA5 <= MA10
# 条件2:今天 MA5 > MA10
is_first_golden_cross = (ma5_yesterday <= ma10_yesterday) and (ma5_today > ma10_today)
# 判断死叉事件(用于卖出)
is_dead_cross = (ma5_yesterday >= ma10_yesterday) and (ma5_today < ma10_today)
# 如果是首次金叉,并且当前没有持仓,并且有可用资金
if is_first_golden_cross and current_position == 0 and cash > 0:
# 全仓买入
order_value(security, cash)
log.info("触发首次金叉!买入 %s" % security)
# 如果出现死叉,并且当前有持仓
elif is_dead_cross and current_position > 0:
# 清仓卖出
order_target(security, 0)
log.info("触发死叉!卖出 %s" % security)
代码解析与进阶技巧
- 数据切片计算均线:
代码中使用了close_prices[-5:].mean()来计算当期均线,使用close_prices[-6:-1].mean()来计算上一期均线。这种切片方法非常高效,避免了调用复杂的指标库。 - 持仓状态过滤 (
current_position == 0):
即使你的策略逻辑在某些极端情况下(例如数据微小波动导致连续两天触发金叉条件),加上current_position == 0的判断可以作为最后一道防线,确保只要手里有票,就不会重复执行买入指令。 - 日线数据的时效性:
在run_daily(..., time='09:30')中调用attribute_history(..., '1d'),获取到的最后一条数据是昨天的日线。因此,策略实际上是在每天开盘时,根据昨天收盘后形成的均线形态来决定今天的交易。