跟量下单算法(Percentage of Volume,POV)(难度:初级)

一、跟量下单算法详解

在算法交易的各种策略中,按执行行为对市场的主动程度,可以将其划分为三类:被动型算法交易、主动型算法交易以及综合型算法交易。 其中,跟量下单(Percentage of Volume, 简称POV)策略被归类为主动型算法交易方法。它通过监测市场成交量的实时变化,动态调整下单节奏,是一种将执行行为与市场活跃度紧密挂钩的策略。

跟量下单(POV)算法的核心思想是:按照市场成交量的一定比例来执行订单。交易者预先设定一个跟量比例(如10%),系统在运行过程中持续获取市场成交量的最新数据,并据此计算当前应执行的交易手数。 例如,当某一时间段内市场成交量新增了500手,设定的比例为10%,那么策略此阶段应成交50手。这种机制可以使策略在市场流动性高时自动加快交易速度,而在流动性较低时放慢执行节奏,从而降低市场冲击和滑点风险。

POV策略的优势较为明显,它能够根据市场的流动性自动调节交易节奏,在流动性充足时迅速完成交易,提升效率;而在市场冷清时则主动减速,降低对盘口的冲击这种“顺势而为”的执行逻辑,使交易行为更加自然,有效提升了执行的隐蔽性和整体质量。 但POV策略也存在一定的局限性。首先,它强依赖市场成交量,在成交量骤降或出现价格急变时,可能会错失最佳交易时机,导致未能及时建仓或平仓。其次,由于严格受限于市场成交量占比,当市场情绪剧烈波动或临近收盘时,其被动性可能导致交易不完全或执行滞后。此外,POV策略的执行路径容易被高频交易者捕捉,在极端市场中存在被“跟单”或“反向打击”的风险,从而降低策略的隐蔽性和执行效率。

二、天勤介绍

天勤平台概述

天勤(TqSdk)是一个由信易科技开发的开源量化交易系统,为期货、期权等衍生品交易提供专业的量化交易解决方案。平台具有以下特 点:

  • 丰富的行情数据 提供所有可交易合约的全部Tick和K线数据,基于内存数据库实现零延迟访问。
  • 一站式的解决方案 从历史数据分析到实盘交易的完整工具链,打通开发、回测、模拟到实盘的全流程。
  • 专业的技术支持 近百个技术指标源码,深度集成pandas和numpy,采用单线程异步模型保证性能。

策略开发流程

  • 环境准备
    • 安装Python环境(推荐Python 3.6或以上版本)
    • 安装tqsdk包:pip install tqsdk
    • 注册天勤账户获取访问密钥
  • 数据准备
    • 订阅近月与远月合约行情
    • 获取历史K线或者Tick数据(用于分析与行情推进)
  • 策略编写
    • 设计信号生成逻辑(基于价差、均值和标准差)
    • 编写交易执行模块(开仓、平仓逻辑)
    • 实现风险控制措施(止损、资金管理)
  • 回测验证
    • 设置回测时间区间和初始资金
    • 运行策略获取回测结果
    • 分析绩效指标(胜率、收益率、夏普率等)
  • 策略优化
    • 调整参数(标准差倍数、窗口大小等)
    • 添加过滤条件(成交量、波动率等)
    • 完善风险控制机制

三、跟量下单算法代码实现

本策略POV基于天勤量化交易平台(TqSdk)开发,使用Python语言进行编写。该策略的设计目标是在市场成交量波动的基础上,精细控制下单节奏与手数,从而在控制市场冲击的前提下完成指定规模的成交任务。

从策略设计角度来看,核心在于如何“以量定速”,即通过追踪市场成交量的变化动态调整订单执行频率。为实现这一点,系统需持续采集并比较市场的累计成交量变化,计算新增成交量,并将其乘以预设的比例系数得到应执行的交易手数。随后根据剩余目标手数进行约束,防止过量提交订单。考虑到不同市场条件下的订单需求与滑点控制,本策略允许用户选择“对价”或“挂价”两种报单方式,以适应不同的流动性环境。

策略在整个执行过程中保持一个清晰的状态更新机制:每轮订单提交后,系统等待其成交完成,再更新成交基准量,并进入下一轮判断与执行。该过程既保证了成交控制的准确性,也为后续扩展(如加滑价保护、延迟处理、成交日志记录等)提供了良好的结构基础。

该策略的实现可分为以下四个步骤:

  1. 设定策略初始参数,包括交易合约代码、方向(买入/卖出)、目标交易总手数、跟量比例,以及报单方式(如“对价”或“挂价”)等。

  2. 初始化变量并读取当前市场累计成交量作为基准值,同时设置已成交手数为零,用于后续累计与判断。

  3. 在策略主循环中,实时监控市场成交量的变化,计算成交量的增量部分,并按设定比例计算应挂出的订单手数;随后根据报单类型选择合适价格提交限价单,并等待订单成交。

  4. 每轮交易完成后更新成交基准和累计成交量,循环执行,直到完成全部目标手数为止。

天勤实现算法代码

#!/usr/bin/env python
# coding=utf-8
__author__ = "Chaos"

from tqsdk import TqApi, TqAuth, TqKq
import math

# === 用户参数 ===
SYMBOL = "SHFE.ag2506"      # 交易合约
DIRECTION = "BUY"           # "BUY"为买入,"SELL"为卖出
OFFSET = "OPEN"             # "OPEN"为开仓,"CLOSE"为平仓,"CLOSETODAY"为平今仓
TOTAL_VOLUME = 30          # 目标总手数
POV_RATIO = 0.1             # 跟量比例(如0.1表示10%)
ORDER_TYPE = "对价"         # "对价"为对价报单,"报价"为挂价报单

# === 初始化API ===
acc = TqKq()
api = TqApi(account=acc, auth=TqAuth("快期账号", "快期密码"))
quote = api.get_quote(SYMBOL)

# === 初始化变量 ===
base_volume = quote.volume  # 启动时的市场累计成交量
traded_volume = 0           # 已成交手数
last_printed_volume = 0     # 上次打印的成交手数

print(f"POV算法启动,合约: {SYMBOL},目标: {TOTAL_VOLUME}手,方向: {DIRECTION},量比比例: {POV_RATIO*100}%")

try:
    while traded_volume < TOTAL_VOLUME:
        api.wait_update()
        new_volume = quote.volume
        delta = new_volume - base_volume
        # 计算本轮应下单手数
        order_volume = int(math.floor(delta * POV_RATIO))
        # 不能超过剩余目标
        order_volume = min(order_volume, TOTAL_VOLUME - traded_volume)
        
        if order_volume > 0:
            print(f"\n市场成交量: {base_volume} -> {new_volume}手")
            print(f"变化量: {delta}手")
            print(f"计算下单手数: {delta} * {POV_RATIO} = {order_volume}手")
            
            # 根据报单类型选择价格
            if ORDER_TYPE == "对价":
                # 对价报单
                price = quote.ask_price1 if DIRECTION == "BUY" else quote.bid_price1
            else:
                # 挂价报单
                price = quote.bid_price1 if DIRECTION == "BUY" else quote.ask_price1
            
            order = api.insert_order(
                symbol=SYMBOL,
                direction=DIRECTION,
                offset=OFFSET,
                volume=order_volume,
                limit_price=price
            )
            print(f"下单: {order_volume}手,价格: {price},报单类型: {ORDER_TYPE}")
            
            # 记录上一次的状态和剩余量
            last_status = order.status
            last_volume_left = order.volume_left
            
            # 等待订单状态更新
            while order.status == "ALIVE":
                api.wait_update()
                # 只在状态或剩余量发生变化时打印
                if order.status != last_status or order.volume_left != last_volume_left:
                    print(f"订单状态更新: {order.status}, 剩余量: {order.volume_left}")
                    last_status = order.status
                    last_volume_left = order.volume_left
            
            # 检查订单是否出错
            if order.is_error:
                print(f"下单失败: {order.last_msg}")
                break
            
            # 计算实际成交量
            actual_trade = order.volume_orign - order.volume_left
            if actual_trade > 0:
                print(f"本轮成交: {actual_trade}手")
                traded_volume += actual_trade
                base_volume = new_volume  # 更新基准成交量
                
                # 只在成交手数变化时打印进度
                if traded_volume > last_printed_volume:
                    print(f"当前进度: {traded_volume} / {TOTAL_VOLUME}")
                    last_printed_volume = traded_volume
            else:
                print(f"订单未成交: {order.last_msg}")
                
    print("POV算法执行完毕")
except Exception as e:
    print(f"捕获到异常: {e}")
finally:
    api.close()