3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个非常实用的策略需求。在 QMT 中,我们可以利用 get_ipo_data 获取当日新股信息,并结合 get_new_purchase_limit 获取账户的申购额度来实现自动打新。
get_ipo_data("STOCK") 获取当天所有可申购的新股列表,包含申购代码、发行价格、申购上限等信息。get_new_purchase_limit(accid) 获取当前资金账号在上海主板、深圳市场、科创板的可用申购额度。min(账户在该市场的可用额度, 该新股的单账户申购上限)。passorder 函数进行申购。申购通常使用“限价”委托,价格为发行价。# -*- coding: gbk -*-
import time
def init(ContextInfo):
# ================= 策略参数设置 =================
# 请在此处填写您的资金账号
ContextInfo.acc_id = 'YOUR_ACCOUNT_ID'
# 账号类型:'STOCK' 代表股票,'CREDIT' 代表信用账户
ContextInfo.acc_type = 'STOCK'
# ==============================================
# 设置交易账号
ContextInfo.set_account(ContextInfo.acc_id)
# 用于记录当天是否已经执行过打新,格式为 'YYYYMMDD'
ContextInfo.last_run_date = ''
print("新股自动申购策略已启动,账号: {}".format(ContextInfo.acc_id))
def handlebar(ContextInfo):
# 仅在实盘的最后一根K线(最新行情)运行
if not ContextInfo.is_last_bar():
return
# 获取当前日期和时间
current_date = time.strftime('%Y%m%d', time.localtime())
current_time = time.strftime('%H%M%S', time.localtime())
# 控制运行时间:建议在 09:45 到 14:50 之间运行,避开集合竞价的不确定性
if not ('094500' <= current_time <= '145000'):
return
# 控制每天只运行一次
if ContextInfo.last_run_date == current_date:
return
print("=" * 30)
print("开始检查今日新股申购信息...")
# 1. 获取当日新股信息
# type="STOCK" 只获取新股,不包含新债
ipo_data = get_ipo_data("STOCK")
if not ipo_data:
print("今日无新股可申购。")
ContextInfo.last_run_date = current_date
return
# 2. 获取账户申购额度
# 返回字典,通常包含 '上海主板', '深圳市场', '上海科创板' 等键
purchase_limit = get_new_purchase_limit(ContextInfo.acc_id)
print("当前账户申购额度: {}".format(purchase_limit))
# 3. 遍历新股并下单
# ipo_data 通常是一个列表,每个元素是一个对象或字典
# 注意:不同券商版本返回结构可能略有差异,以下代码基于标准结构编写
for stock in ipo_data:
# 获取新股基本信息
# 注意:这里假设 stock 是对象,如果报错请尝试 stock['instrument']
try:
code = stock.instrument # 申购代码,如 732xxx.SH
name = stock.name # 股票名称
price = stock.issuePrice # 发行价格
max_qty = stock.maxPurchaseNum # 单账户申购上限
except:
# 兼容字典访问方式
code = stock.get('instrument')
name = stock.get('name')
price = stock.get('issuePrice')
max_qty = stock.get('maxPurchaseNum')
if not code:
continue
# 4. 判断市场并获取对应额度
market_limit = 0
market_name = ""
if code.endswith('.SH'):
if code.startswith('787') or code.startswith('688'): # 科创板申购代码通常以787开头,股票代码688
market_name = '上海科创板'
market_limit = purchase_limit.get(market_name, 0)
else:
market_name = '上海主板'
market_limit = purchase_limit.get(market_name, 0)
elif code.endswith('.SZ'):
# 深圳市场(包含主板、创业板)额度通常合并计算
market_name = '深圳市场'
market_limit = purchase_limit.get(market_name, 0)
# 5. 计算最终申购数量
# 申购量 = min(账户额度, 新股申购上限)
# 必须是 500 (科创板/沪市) 或 500 (深市) 的倍数,这里简化处理,直接取最小值
# 实际上柜台会自动处理非整数倍的废单,或者我们可以向下取整
if market_limit <= 0:
print("股票: {} ({}) - 跳过,{} 额度不足 (额度: {})".format(name, code, market_name, market_limit))
continue
# 确定下单数量
volume = min(market_limit, max_qty)
# 再次确保数量大于0
if volume <= 0:
continue
print("正在申购: {} ({}) | 价格: {} | 数量: {}".format(name, code, price, volume))
# 6. 下单
# opType=23 (买入)
# orderType=1101 (单股单账号普通下单)
# prType=11 (指定价/模型价),配合 price 参数使用
passorder(23, 1101, ContextInfo.acc_id, code, 11, price, volume, ContextInfo)
# 标记今日已运行
ContextInfo.last_run_date = current_date
print("今日新股申购任务执行完毕。")
print("=" * 30)
get_ipo_data("STOCK"):
get_new_purchase_limit(accid):
{'上海主板': 10000, '深圳市场': 5000, '上海科创板': 0}。额度匹配逻辑:
.SH, .SZ) 和前缀 (688, 787) 来区分是上海主板、科创板还是深圳市场,从而去匹配 purchase_limit 字典中对应的额度。passorder 下单:
opType=23: 代表股票买入(申购本质上是买入行为)。prType=11: 代表指定价格(Limit Price)。新股申购必须严格按照发行价申购,所以我们将 price 参数设置为 stock.issuePrice。防重复机制:
ContextInfo.last_run_date 变量。策略在 handlebar 中每根 K 线(或 Tick)都会被触发,但我们通过对比日期,保证每天只执行一次核心逻辑。ContextInfo.acc_id:将 'YOUR_ACCOUNT_ID' 替换为你真实的资金账号。Q: 为什么 get_new_purchase_limit 返回的额度是 0?
A: 这通常意味着你的账户在 T-2 日前 20 个交易日的日均持仓市值不足(沪市需 1 万元以上,深市需 1 万元以上),或者数据尚未更新。建议在交易时间(9:30 后)运行策略以获取准确数据。
Q: 策略报错 AttributeError: 'dict' object has no attribute 'instrument' 怎么办?
A: 不同券商的 QMT 底层数据结构可能微调(有的返回对象,有的返回字典)。我在代码中加入了 try...except 块来同时兼容对象属性访问(stock.instrument)和字典键值访问(stock.get('instrument')),这应该能解决大部分兼容性问题。
Q: 申购数量需要自己计算 500/1000 的倍数吗?
A: 严格来说是的(沪市主板 1000 股整数倍,科创板/深市 500 股整数倍)。但在实际操作中,如果你的下单数量不满足整数倍,柜台系统通常会自动拒绝或向下取整。上述代码为了简洁,直接取了 min(额度, 上限)。如果需要更严谨,可以增加取整逻辑,例如 volume = int(volume / 500) * 500。