4、时间切片策略:固定时间间隔、动态时间间隔、成交量加权时间间隔
时间切片策略,说白了就是解决一个核心问题:我该在什么时间点把大单子扔出去?
你想想看,如果一口气把10000股砸进盘口,那价格肯定被打穿。但如果你把它切成几十个小块,每个小块选一个合适的时机发出去,效果就完全不一样了。这就是时间切片要做的事。
我个人习惯把时间切片策略分成三大类:固定间隔、动态间隔、成交量加权间隔。咱们一个一个聊。
4.1 固定时间间隔
这是最朴素的做法。说白了就是:每隔X秒发一笔单子,雷打不动。
比如你手里有10000股要卖,计划在10分钟内完成。那就每6秒发一笔,每笔大概100股。代码实现起来也简单:
class FixedTimeSlice:
def __init__(self, total_qty, total_seconds, interval=6):
self.total_qty = total_qty
self.total_seconds = total_seconds
self.interval = interval
self.slice_count = total_seconds // interval
self.slice_qty = total_qty // self.slice_count
def next_slice(self, current_time):
"""返回当前时间片应该发送的数量"""
elapsed = current_time - self.start_time
if elapsed >= self.total_seconds:
return 0
slice_index = int(elapsed // self.interval)
return self.slice_qty
嗯,这里要注意:固定间隔有个致命缺陷——容易被市场识别。我在项目中遇到过,某个做市商发现我们的单子每3秒出现一次,直接在我们前面挂单吃差价。那叫一个难受。
4.2 动态时间间隔
固定间隔太死板,那就让它动起来。动态时间间隔的核心思路是:根据市场状态调整发单节奏。
怎么调?我一般看两个指标:
- 波动率:波动大的时候,间隔拉长,别在剧烈波动时冲进去
- 流动性:流动性好的时候,间隔缩短,快速完成
举个例子:
class DynamicTimeSlice:
def __init__(self, total_qty, base_interval=5):
self.total_qty = total_qty
self.base_interval = base_interval
self.remaining_qty = total_qty
def calc_interval(self, volatility, liquidity_score):
"""
volatility: 当前波动率(0~1)
liquidity_score: 流动性评分(0~1,越高越好)
"""
# 波动大 -> 间隔拉长
vol_factor = 1 + volatility * 2
# 流动性好 -> 间隔缩短
liq_factor = 2 - liquidity_score
return self.base_interval * vol_factor / liq_factor
def next_slice(self, current_time, volatility, liquidity_score):
interval = self.calc_interval(volatility, liquidity_score)
# 计算本次应发数量
slice_qty = min(self.remaining_qty,
int(self.total_qty * interval / self.total_time))
self.remaining_qty -= slice_qty
return slice_qty, interval
动态间隔的好处是灵活,但有个坑:参数太多,容易过拟合。我记得有一次调了十几个参数,回测漂亮得不行,实盘一跑直接崩了。后来学乖了,最多调3个核心参数。
4.3 成交量加权时间间隔
这个是我个人最喜欢的一种。它的逻辑很简单:市场成交量大的时候多发,成交量小的时候少发。
为什么?因为成交量大的时候,你的单子更容易被淹没在订单流里,不容易被发现。而且流动性好,冲击成本也低。
具体做法:
class VolumeWeightedTimeSlice:
def __init__(self, total_qty, target_participation=0.1):
self.total_qty = total_qty
self.target_participation = target_participation # 目标参与率
self.remaining_qty = total_qty
def next_slice(self, current_time, recent_volume):
"""
recent_volume: 最近一段时间(比如1分钟)的市场成交量
"""
# 目标参与率 * 市场成交量 = 本次应发数量
target_qty = int(self.target_participation * recent_volume)
# 但不能超过剩余量
slice_qty = min(self.remaining_qty, target_qty)
self.remaining_qty -= slice_qty
return slice_qty
这里有个关键参数:目标参与率。设得太高,你的单子就成了市场的主要推动力;设得太低,完成时间会拖得很长。
我一般设0.05~0.15之间。具体看品种:
| 品种类型 | 建议参与率 | 说明 |
|---|---|---|
| 大盘蓝筹股 | 0.08~0.12 | 流动性好,可以稍微激进 |
| 中小盘股 | 0.03~0.06 | 流动性差,保守一点 |
| 期货主力合约 | 0.10~0.15 | 深度好,可以适当提高 |
| 加密货币 | 0.02~0.05 | 波动大,小心为上 |
4.4 三种策略对比
我把三种策略的核心差异整理了一下:
| 维度 | 固定间隔 | 动态间隔 | 成交量加权 |
|---|---|---|---|
| 实现难度 | 低 | 中 | 中高 |
| 市场适应性 | 差 | 中 | 好 |
| 被识破风险 | 高 | 中 | 低 |
| 参数数量 | 1~2个 | 3~5个 | 2~3个 |
| 适用场景 | 小单、低要求 | 中等规模 | 大单、高要求 |
4.5 核心逻辑流程图
下面这张图展示了三种策略的核心决策逻辑:
4.6 实战建议
说了这么多,到底怎么选?我个人的经验是:
- 小单(< 总成交量1%):固定间隔就够了,别折腾
- 中单(1%~5%):动态间隔,稍微加点自适应逻辑
- 大单(> 5%):成交量加权,这是最稳妥的选择
最后说一句:没有万能策略。我见过有人用固定间隔在某个品种上跑得很好,换到另一个品种就崩了。所以,多测试、多对比,找到适合你交易品种的那一款。
公众号:蓝海资料掘金营,微信deep3321