问题描述
解决方案
这是一个基于**星期效应(Weekend Effect)**的交易策略请求。该策略的核心逻辑非常清晰:利用日历效应,在周五买入持有过周末,并在周一卖出。
策略设计思路
- 运行周期:建议使用 日线(1d) 周期。
- 核心逻辑:
- 获取当前 K 线的时间。
- 判断当前日期是星期几。
- 如果是 星期五:全仓买入(持有过周末)。
- 如果是 星期一:清仓卖出(获利了结或止损)。
- 其他时间:空仓观望。
- 使用的 API:
get_bar_timetag和timetag_to_datetime:用于获取和转换时间。datetime库:用于判断星期几(0代表周一,4代表周五)。order_target_percent:用于目标仓位管理(1.0 代表满仓,0.0 代表空仓)。
QMT 策略代码实现
# -*- coding: gbk -*-
import datetime
def init(ContextInfo):
"""
初始化函数,设定资金账号、标的和其他参数
"""
# 1. 设置标的股票,这里以沪深300ETF(510300.SH)为例
ContextInfo.stock_code = '510300.SH'
# 2. 设置股票池
ContextInfo.set_universe([ContextInfo.stock_code])
# 3. 设置资金账号 (请替换为您真实的资金账号)
ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
ContextInfo.set_account(ContextInfo.account_id)
# 4. 设置策略运行参数
# 0: 周一, 1: 周二, ..., 4: 周五, 5: 周六, 6: 周日
ContextInfo.buy_day = 4 # 周五
ContextInfo.sell_day = 0 # 周一
def handlebar(ContextInfo):
"""
K线逐根运行函数
"""
# 获取当前K线的索引位置
index = ContextInfo.barpos
# 获取当前K线的时间戳 (毫秒)
timetag = ContextInfo.get_bar_timetag(index)
# 将时间戳转换为字符串格式 'YYYYMMDD'
date_str = timetag_to_datetime(timetag, '%Y%m%d')
# 将字符串转换为 datetime 对象,以便获取星期几
try:
current_date = datetime.datetime.strptime(date_str, '%Y%m%d')
except:
# 防止解析错误
return
# 获取星期几 (0=周一, 4=周五)
weekday = current_date.weekday()
# 获取当前标的
stock = ContextInfo.stock_code
# --- 交易逻辑 ---
# 情况1:如果是周五,且当前没有满仓,则买入
if weekday == ContextInfo.buy_day:
# 使用 order_target_percent 调整至 100% 仓位
# 注意:实盘中建议检查 is_last_bar() 避免历史K线重复发单,
# 但 order_target_percent 本身具有幂等性(即已经是满仓则不会重复买)
order_target_percent(stock, 1.0, ContextInfo, ContextInfo.account_id)
# 仅在最后一根K线打印日志,避免回测日志过多
if ContextInfo.is_last_bar():
print(f"日期: {date_str} (周五) - 触发买入信号: {stock}")
# 情况2:如果是周一,且当前持有仓位,则卖出
elif weekday == ContextInfo.sell_day:
# 使用 order_target_percent 调整至 0% 仓位 (清仓)
order_target_percent(stock, 0.0, ContextInfo, ContextInfo.account_id)
if ContextInfo.is_last_bar():
print(f"日期: {date_str} (周一) - 触发卖出信号: {stock}")
# 情况3:其他时间 (周二、三、四),保持空仓 (如果策略要求严格只持仓周末)
else:
# 确保仓位为0 (防止手动干预后策略状态不对)
# 如果您希望周一卖出后,周二到周四可以做其他操作,请注释掉下面这行
order_target_percent(stock, 0.0, ContextInfo, ContextInfo.account_id)
代码详解与注意事项
-
编码格式:
代码第一行# -*- coding: gbk -*-是必须的,因为 QMT 的 Python 环境默认使用 GBK 编码,防止中文注释乱码。 -
时间处理:
ContextInfo.get_bar_timetag(index)获取的是毫秒级时间戳。timetag_to_datetime是 QMT 内置函数,将其转为字符串。datetime.datetime.strptime将字符串转为 Python 的日期对象,从而可以使用.weekday()方法。
-
交易函数选择:
- 使用了
order_target_percent。这是一个非常方便的高级函数。 1.0表示调整仓位至总资产的 100%(全仓买入)。0.0表示调整仓位至 0%(全仓卖出)。- 相比于
passorder,这个函数自动计算需要买卖的数量,非常适合这种简单的资产配置策略。
- 使用了
-
回测设置建议:
- 周期:日线。
- 手续费:由于该策略每周进行一次买卖(高频周转),交易成本(印花税、佣金)对净值影响巨大。回测时请务必设置真实的费率(如万分之二或万分之三)。
- 成交价格:在日线回测模式下,QMT 通常以当根 K 线的收盘价作为成交价。这意味着策略逻辑是“周五收盘买入,周一收盘卖出”。
-
实盘/模拟盘提示:
- 如果在实盘运行,日线周期下,
handlebar通常在行情更新时触发。 - 为了确保在周五收盘前成交,您可能需要结合
ContextInfo.run_time定时器功能,或者在盘中(例如 14:55)运行策略检查信号。上述代码在日线图上运行时,通常会在收盘后或盘中最新价触发,具体取决于您的运行模式设置。
- 如果在实盘运行,日线周期下,
常见问题 (Q&A)
Q: 如果周一休市(例如端午节),策略会怎么做?
A: 按照上述代码逻辑,如果周一没有 K 线数据,代码就不会运行。到了周二(weekday=1),代码会进入 else 分支执行 order_target_percent(stock, 0.0),即在周二卖出。这符合“非周五即空仓”的逻辑。
Q: 我想在周一开盘就卖出,而不是收盘,怎么改?
A: 在 QMT 的回测中,日线级别很难精确控制“开盘买/卖”。如果需要更精细的控制,建议将回测周期改为 分钟线(如 15m 或 1h)。
在分钟线逻辑中,你需要判断:if weekday == 0 and (是当天的第一根K线): 卖出。
Q: 如何更换交易标的?
A: 修改 init 函数中的 ContextInfo.stock_code = '510300.SH',将其改为您想交易的股票代码(如 '600519.SH')即可。