【期货量化实战】日内动量策略:顺势而为的短线交易法(Python源码)

一、前言

动量效应是市场中最持久的规律之一:强者恒强,弱者恒弱。日内动量策略就是在短期内捕捉价格的惯性运动,顺势而为。

本文将介绍:

  • 动量指标的计算方法
  • 动量信号的实战应用
  • 多周期动量确认
  • 完整策略代码与回测

二、为什么选择天勤量化(TqSdk)

开发量化交易策略需要功能完整的框架。**天勤量化(TqSdk)**是目前国内最适合个人投资者的期货量化平台:

功能 说明
模拟交易 内置TqSim模拟盘,无需开户即可测试
回测功能 支持历史数据回测
实盘对接 一套代码模拟实盘通用
多周期数据 支持分钟、小时等多种K线周期

安装方法

bash 复制代码
pip install tqsdk

重要提示:建议先使用模拟盘充分测试策略,确认无误后再考虑实盘交易。

三、动量指标详解

3.1 动量的计算

类型 计算公式 说明
简单动量 当前价 - N周期前价格 绝对动量
动量百分比 (当前价 - N周期前价格) / N周期前价格 × 100 相对动量
ROC (当前价 / N周期前价格 - 1) × 100 变化率

3.2 动量的含义

动量状态 含义 交易信号
动量 > 0 且上升 上涨动能增强 考虑做多
动量 > 0 且下降 上涨动能减弱 注意止盈
动量 < 0 且下降 下跌动能增强 考虑做空
动量 < 0 且上升 下跌动能减弱 注意止盈

3.3 日内策略特点

特点 说明
交易频率高 日内多次进出
持仓时间短 几分钟到几小时
不持仓过夜 避免隔夜风险
对延迟敏感 需要快速执行

四、基础版动量策略

4.1 完整代码

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
功能:日内动量策略
说明:本代码仅供学习参考
"""

from tqsdk import TqApi, TqAuth, TqSim
from tqsdk.lib import TargetPosTask
import datetime

# ============ 策略参数 ============
SYMBOL = "SHFE.rb2510"      # 交易合约
KLINE_PERIOD = 60           # 1分钟K线
MOM_PERIOD = 10             # 动量周期
MOM_THRESHOLD = 0.3         # 动量阈值(百分比)
VOLUME = 1                  # 交易手数

# 日盘交易时间
DAY_OPEN = datetime.time(9, 0)
DAY_CLOSE = datetime.time(15, 0)

# ============ 初始化 ============
api = TqApi(TqSim(), auth=TqAuth("快期账户", "快期密码"))
klines = api.get_kline_serial(SYMBOL, KLINE_PERIOD, MOM_PERIOD + 50)
target_pos = TargetPosTask(api, SYMBOL)
position = api.get_position(SYMBOL)

print("=" * 60)
print("日内动量策略")
print("=" * 60)
print(f"交易合约: {SYMBOL}")
print(f"动量周期: {MOM_PERIOD}, 阈值: {MOM_THRESHOLD}%")
print("-" * 60)

def calc_momentum(close, period):
    """计算动量百分比"""
    mom = (close - close.shift(period)) / close.shift(period) * 100
    return mom

prev_mom = None

while True:
    api.wait_update()
    
    if api.is_changing(klines.iloc[-1], "datetime"):
        current_dt = datetime.datetime.fromtimestamp(klines.iloc[-1]["datetime"] / 1e9)
        current_time = current_dt.time()
        current_price = klines["close"].iloc[-1]
        
        # 计算动量
        mom = calc_momentum(klines["close"], MOM_PERIOD)
        current_mom = mom.iloc[-1]
        
        current_pos = position.pos_long - position.pos_short
        
        # 只在交易时间内交易
        if DAY_OPEN <= current_time <= DAY_CLOSE:
            # 动量状态
            if current_mom > MOM_THRESHOLD:
                status = "🔴上涨动量"
            elif current_mom < -MOM_THRESHOLD:
                status = "🟢下跌动量"
            else:
                status = "⚪动量平稳"
            
            print(f"[{current_time}] {status} | 动量={current_mom:.2f}% | 价格={current_price:.0f}")
            
            if prev_mom is not None:
                # 无持仓
                if current_pos == 0:
                    # 动量向上突破阈值,做多
                    if current_mom > MOM_THRESHOLD and prev_mom <= MOM_THRESHOLD:
                        print(f"         ★ 动量上穿阈值,开多")
                        target_pos.set_target_volume(VOLUME)
                    
                    # 动量向下突破阈值,做空
                    elif current_mom < -MOM_THRESHOLD and prev_mom >= -MOM_THRESHOLD:
                        print(f"         ★ 动量下穿阈值,开空")
                        target_pos.set_target_volume(-VOLUME)
                
                # 持有多单,动量转负平仓
                elif current_pos > 0:
                    if current_mom < 0:
                        print(f"         ✖ 动量转负,平多")
                        target_pos.set_target_volume(0)
                
                # 持有空单,动量转正平仓
                elif current_pos < 0:
                    if current_mom > 0:
                        print(f"         ✖ 动量转正,平空")
                        target_pos.set_target_volume(0)
            
            # 收盘前平仓
            if current_time >= datetime.time(14, 55):
                if current_pos != 0:
                    print(f"         ⏰ 收盘前平仓")
                    target_pos.set_target_volume(0)
        
        prev_mom = current_mom

4.2 代码解析

代码 说明
close.shift(period) 获取N周期前的价格
(close - close.shift(period)) / close.shift(period) 计算变化百分比
MOM_THRESHOLD = 0.3 动量变化超过0.3%才算有效

4.3 运行效果

复制代码
============================================================
日内动量策略
============================================================
交易合约: SHFE.rb2510
动量周期: 10, 阈值: 0.3%
------------------------------------------------------------
[09:05:00] ⚪动量平稳 | 动量=0.12% | 价格=3282
[09:06:00] ⚪动量平稳 | 动量=0.18% | 价格=3285
[09:07:00] 🔴上涨动量 | 动量=0.35% | 价格=3292
         ★ 动量上穿阈值,开多
...

五、增强版(双周期动量确认)

5.1 策略思路

使用两个周期的动量指标相互确认:

指标 周期 作用
快速动量 5 提供交易信号
慢速动量 20 确认趋势方向

只有当快慢动量同向时才入场。

5.2 完整代码

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
功能:双周期动量策略
说明:本代码仅供学习参考
"""

from tqsdk import TqApi, TqAuth, TqSim
from tqsdk.lib import TargetPosTask
import datetime

# ============ 策略参数 ============
SYMBOL = "SHFE.rb2510"
KLINE_PERIOD = 60
MOM_FAST = 5                # 快速动量周期
MOM_SLOW = 20               # 慢速动量周期
MOM_THRESHOLD = 0.2         # 动量阈值
STOP_LOSS_PCT = 0.5         # 止损百分比
VOLUME = 1

DAY_OPEN = datetime.time(9, 0)
DAY_CLOSE = datetime.time(15, 0)

# ============ 初始化 ============
api = TqApi(TqSim(), auth=TqAuth("快期账户", "快期密码"))
klines = api.get_kline_serial(SYMBOL, KLINE_PERIOD, max(MOM_FAST, MOM_SLOW) + 50)
target_pos = TargetPosTask(api, SYMBOL)
position = api.get_position(SYMBOL)

print("=" * 60)
print("双周期动量策略")
print("=" * 60)
print(f"交易合约: {SYMBOL}")
print(f"快速动量: {MOM_FAST}周期, 慢速动量: {MOM_SLOW}周期")
print("-" * 60)

def calc_momentum(close, period):
    return (close - close.shift(period)) / close.shift(period) * 100

# 状态变量
prev_mom_fast = None
prev_mom_slow = None
entry_price = 0

while True:
    api.wait_update()
    
    if api.is_changing(klines.iloc[-1], "datetime"):
        current_dt = datetime.datetime.fromtimestamp(klines.iloc[-1]["datetime"] / 1e9)
        current_time = current_dt.time()
        current_price = klines["close"].iloc[-1]
        
        # 计算双周期动量
        mom_fast = calc_momentum(klines["close"], MOM_FAST)
        mom_slow = calc_momentum(klines["close"], MOM_SLOW)
        
        curr_mom_fast = mom_fast.iloc[-1]
        curr_mom_slow = mom_slow.iloc[-1]
        
        current_pos = position.pos_long - position.pos_short
        
        if DAY_OPEN <= current_time <= DAY_CLOSE:
            # 动量共振判断
            both_positive = curr_mom_fast > MOM_THRESHOLD and curr_mom_slow > 0
            both_negative = curr_mom_fast < -MOM_THRESHOLD and curr_mom_slow < 0
            
            if prev_mom_fast is not None:
                # 无持仓
                if current_pos == 0:
                    # 双周期同时看多
                    if both_positive and prev_mom_fast <= MOM_THRESHOLD:
                        print(f"[{current_time}] 快动量={curr_mom_fast:.2f}% 慢动量={curr_mom_slow:.2f}%")
                        print(f"         ★ 双周期共振,开多")
                        entry_price = current_price
                        target_pos.set_target_volume(VOLUME)
                    
                    # 双周期同时看空
                    elif both_negative and prev_mom_fast >= -MOM_THRESHOLD:
                        print(f"[{current_time}] 快动量={curr_mom_fast:.2f}% 慢动量={curr_mom_slow:.2f}%")
                        print(f"         ★ 双周期共振,开空")
                        entry_price = current_price
                        target_pos.set_target_volume(-VOLUME)
                
                # 持有多单
                elif current_pos > 0:
                    # 止损
                    stop_price = entry_price * (1 - STOP_LOSS_PCT / 100)
                    if current_price < stop_price:
                        print(f"[{current_time}] ✖ 触发止损,平多")
                        target_pos.set_target_volume(0)
                    # 动量反转平仓
                    elif curr_mom_fast < 0 and curr_mom_slow < 0:
                        print(f"[{current_time}] ✖ 双周期反转,平多")
                        target_pos.set_target_volume(0)
                
                # 持有空单
                elif current_pos < 0:
                    stop_price = entry_price * (1 + STOP_LOSS_PCT / 100)
                    if current_price > stop_price:
                        print(f"[{current_time}] ✖ 触发止损,平空")
                        target_pos.set_target_volume(0)
                    elif curr_mom_fast > 0 and curr_mom_slow > 0:
                        print(f"[{current_time}] ✖ 双周期反转,平空")
                        target_pos.set_target_volume(0)
            
            # 收盘前平仓
            if current_time >= datetime.time(14, 55):
                if current_pos != 0:
                    print(f"[{current_time}] ⏰ 收盘前平仓")
                    target_pos.set_target_volume(0)
        
        prev_mom_fast = curr_mom_fast
        prev_mom_slow = curr_mom_slow

六、策略回测

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
功能:日内动量策略回测
说明:本代码仅供学习参考
"""

from tqsdk import TqApi, TqAuth, TqSim, TqBacktest, BacktestFinished
from tqsdk.lib import TargetPosTask
from datetime import date, datetime, time

SYMBOL = "SHFE.rb2501"
START_DATE = date(2024, 1, 1)
END_DATE = date(2024, 6, 30)

MOM_PERIOD = 10
MOM_THRESHOLD = 0.3
VOLUME = 1

acc = TqSim(init_balance=100000)
backtest = TqBacktest(start_dt=START_DATE, end_dt=END_DATE)

def calc_momentum(close, period):
    return (close - close.shift(period)) / close.shift(period) * 100

try:
    api = TqApi(acc, backtest=backtest, auth=TqAuth("快期账户", "快期密码"))
    klines = api.get_kline_serial(SYMBOL, 60, MOM_PERIOD + 50)
    target_pos = TargetPosTask(api, SYMBOL)
    position = api.get_position(SYMBOL)
    
    prev_mom = None
    trade_count = 0
    
    while True:
        api.wait_update()
        
        if api.is_changing(klines.iloc[-1], "datetime"):
            current_dt = datetime.fromtimestamp(klines.iloc[-1]["datetime"] / 1e9)
            current_time = current_dt.time()
            
            mom = calc_momentum(klines["close"], MOM_PERIOD)
            current_mom = mom.iloc[-1]
            current_pos = position.pos_long - position.pos_short
            
            if time(9, 0) <= current_time <= time(15, 0):
                if prev_mom is not None:
                    if current_pos == 0:
                        if current_mom > MOM_THRESHOLD and prev_mom <= MOM_THRESHOLD:
                            target_pos.set_target_volume(VOLUME)
                            trade_count += 1
                        elif current_mom < -MOM_THRESHOLD and prev_mom >= -MOM_THRESHOLD:
                            target_pos.set_target_volume(-VOLUME)
                            trade_count += 1
                    elif current_pos > 0 and current_mom < 0:
                        target_pos.set_target_volume(0)
                    elif current_pos < 0 and current_mom > 0:
                        target_pos.set_target_volume(0)
                
                if current_time >= time(14, 55):
                    if current_pos != 0:
                        target_pos.set_target_volume(0)
            
            prev_mom = current_mom

except BacktestFinished:
    print("=" * 60)
    print("日内动量策略回测结果")
    print("=" * 60)
    
    stat = acc.tqsdk_stat
    print(f"回测区间: {START_DATE} ~ {END_DATE}")
    print(f"动量参数: 周期{MOM_PERIOD}, 阈值{MOM_THRESHOLD}%")
    print("-" * 60)
    print(f"交易次数: {trade_count}")
    print(f"收益率: {stat['ror']*100:.2f}%")
    print(f"最大回撤: {stat['max_drawdown']*100:.2f}%")
    print(f"夏普比率: {stat['sharpe_ratio']:.2f}")
    print(f"胜率: {stat['winning_rate']*100:.2f}%")
    print("=" * 60)
    
    api.close()

七、参数优化建议

市场状态 动量周期 阈值 说明
高波动 5-10 0.5% 快速反应
中等波动 10-15 0.3% 平衡选择
低波动 15-20 0.2% 减少噪音

八、总结

要点 内容
核心思想 顺势而为,追随强势
入场条件 动量突破阈值
出场条件 动量反转或收盘
增强方法 双周期动量确认
适用场景 趋势明显的日内行情

免责声明:本文仅供学习交流使用,不构成任何投资建议。期货交易有风险,入市需谨慎。

更多资源

相关推荐
哈里谢顿20 小时前
记录一次sql优化记录
mysql
POLITE320 小时前
Leetcode 23. 合并 K 个升序链表 (Day 12)
算法·leetcode·链表
APIshop20 小时前
Python 爬虫获取 item_get_web —— 淘宝商品 SKU、详情图、券后价全流程解析
前端·爬虫·python
楚来客20 小时前
AI基础概念之八:Transformer算法通俗解析
人工智能·算法·transformer
风送雨20 小时前
FastMCP 2.0 服务端开发教学文档(下)
服务器·前端·网络·人工智能·python·ai
Chasing Aurora20 小时前
数据库连接+查询优化
数据库·sql·mysql·prompt·约束
效率客栈老秦20 小时前
Python Trae提示词开发实战(8):数据采集与清洗一体化方案让效率提升10倍
人工智能·python·ai·提示词·trae
倔强的石头_20 小时前
【金仓数据库】ksql 指南(六)—— 创建与管理用户和权限(KingbaseES 安全控制核心)
数据库
哈里谢顿20 小时前
一条 Python 语句在 C 扩展里到底怎么跑
python