问题描述
解决方案
这是一个基于PTrade框架的新股上市首日突破策略。
策略逻辑说明
- 筛选新股:在每日盘前(
before_trading_start),获取全市场股票信息,筛选出上市日期(listed_date)等于当前交易日期的股票。 - 设定突破阈值:设定一个突破比例(例如开盘价的5%)。
- 盘中监控:在分钟级别的
handle_data中,监控新股的当前价格。 - 触发交易:如果
当前价格 > 开盘价 * (1 + 突破比例),且当前没有持仓,则执行买入操作。
注意事项
- 回测限制:PTrade回测模式不支持
get_snapshot(快照数据),因此代码中使用data[security].close获取当前分钟收盘价来模拟最新价。 - 实盘差异:如果是实盘,建议使用
tick_data函数配合get_snapshot进行更灵敏的Tick级别监控。下面的代码是基于分钟级别(handle_data)编写的,通用性更强,既可回测也可实盘。 - 风险提示:新股(特别是注册制新股)上市首日波动极大,且可能没有涨跌幅限制,请务必注意风险控制。
策略代码
import datetime
def initialize(context):
"""
初始化函数,设置策略参数
"""
# 设定突破比例,例如 0.05 代表价格超过开盘价 5% 时买入
g.breakout_ratio = 0.05
# 单只股票买入金额
g.trade_value = 50000
# 用于存储当日上市的新股列表
g.today_new_stocks = []
# 开启盘前运行,以便筛选新股
set_parameters(holiday_not_do_before="0")
log.info("策略初始化完成,突破阈值设定为开盘价的: +{}%".format(g.breakout_ratio * 100))
def before_trading_start(context, data):
"""
盘前处理:筛选当日上市的新股
"""
# 重置当日新股列表
g.today_new_stocks = []
# 获取当前回测/交易日期的字符串格式 YYYY-MM-DD
current_date_str = context.blotter.current_dt.strftime("%Y-%m-%d")
# 获取当日A股所有代码
# 注意:全市场扫描在回测中可能较慢,实盘中通常只关注特定列表或通过其他方式获取
all_stocks = get_Ashares()
# 批量获取股票基础信息(包含上市日期)
# 为了提高效率,这里演示全市场获取,实际使用建议缩小范围
stock_infos = get_stock_info(all_stocks)
for stock in all_stocks:
info = stock_infos.get(stock)
if info:
listed_date = info.get('listed_date')
# 如果上市日期等于今天,加入列表
if listed_date == current_date_str:
g.today_new_stocks.append(stock)
if len(g.today_new_stocks) > 0:
log.info("今日上市新股: {}".format(g.today_new_stocks))
# 将新股加入股票池,确保handle_data能收到数据
set_universe(g.today_new_stocks)
else:
log.info("今日无新股上市。")
set_universe([])
def handle_data(context, data):
"""
盘中处理:监控价格并下单
"""
# 如果没有新股,直接返回
if not g.today_new_stocks:
return
for stock in g.today_new_stocks:
# 确保数据存在
if stock not in data:
continue
# 获取当前价格和开盘价
# 在分钟回测中,close为该分钟收盘价;实盘中为最新推送到策略的价格
current_price = data[stock].close
open_price = data[stock].open
# 异常数据保护:如果开盘价为0或空,跳过
if open_price is None or open_price <= 0:
continue
# 计算目标突破价格
target_price = open_price * (1 + g.breakout_ratio)
# 获取当前该股票的持仓
position = get_position(stock)
# 交易逻辑:
# 1. 当前价格大于目标价格
# 2. 当前该股票持仓为0(避免重复买入)
if current_price > target_price and position.amount == 0:
log.info("股票 {} 触发突破买入!开盘价: {}, 当前价: {}, 目标价: {}".format(
stock, open_price, current_price, target_price
))
# 下单买入指定金额
# 注意:新股首日可能波动剧烈,市价单可能成交在不可预期的价格
# 这里使用限价单保护,或者直接用 order_value (通常默认为市价或最新价)
# 为了演示简单,这里使用 order_value
order_id = order_value(stock, g.trade_value)
if order_id:
log.info("已发送买入指令,订单ID: {}".format(order_id))
else:
log.warning("下单失败,可能是资金不足或停牌")
def after_trading_end(context, data):
"""
盘后处理
"""
if len(g.today_new_stocks) > 0:
log.info("今日交易结束,监控新股列表: {}".format(g.today_new_stocks))
常见问题 (Q&A)
Q: 为什么回测时没有买入?
A:
- 请检查回测时间段内是否有新股上市。
- PTrade回测中,如果股票上市首日数据缺失(某些数据源可能不包含首日分钟线),会导致无法触发。
- 检查
g.breakout_ratio是否设置过高,导致价格未达到触发条件。
Q: 如何在实盘中让反应速度更快?
A:
实盘建议使用 tick_data(context, data) 函数替代 handle_data。tick_data 每3秒运行一次(取决于行情推送),可以捕捉更细微的价格变动。在 tick_data 中,你可以使用 get_snapshot(stock) 来获取最新的买一卖一价格。
Q: 代码中的 get_Ashares() 会不会太慢?
A:
在回测中,每天调用一次 get_Ashares() 获取全市场几千只股票并检查上市日期可能会有轻微的性能影响。在实盘中,通常你已经知道今天要上市的新股代码,可以直接在 initialize 或通过外部文件读取代码列表,直接 set_universe(['新股代码']),这样效率最高。
Q: 注册制新股(科创板/创业板)首日没有涨跌幅限制,策略需要调整吗?
A:
本策略正是适用于无涨跌幅限制或涨幅限制较宽(如44%)的场景。如果是主板旧规则(首日涨停44%且很难买入),这个策略可能很难成交,因为开盘即秒板。对于注册制新股,价格波动大,突破买入策略更为有效,但也伴随更高风险,建议配合止损逻辑使用。