25、订单流与机器学习:用订单流数据训练分类/回归模型
说实话,做了这么多年订单流分析,我越来越觉得传统规则策略有天花板。你想想看,人工定义的支撑阻力、失衡、Delta背离,这些模式虽然有效,但市场在变,参数需要不断调。后来我开始尝试把订单流数据喂给机器学习模型,让算法自己去发现规律。效果嘛,比我想象中要好。
这一章,我就带你走一遍完整的流程——从订单流数据清洗、特征工程,到训练一个分类模型(预测方向)和一个回归模型(预测幅度)。代码我会贴出来,你直接拿去跑就行。
为什么订单流数据适合机器学习?
订单流数据本质上是高频的、多维度的市场微观结构数据。每一笔Tick都包含了价格、成交量、买卖方向、挂单深度等信息。这些数据有几个特点:
- 高信噪比:相比裸K线,订单流数据包含了更多未成交的博弈信息
- 时序依赖性:当前时刻的订单流特征往往与后续价格运动有因果关系
- 非线性关系:比如大单吃单后,有时会引发跟风,有时会引发反转,规则很难描述
我个人习惯把订单流数据看作「市场的X光片」。机器学习模型就是那个读片的医生。你喂给它足够多的病例,它就能学会哪些「阴影」是良性的,哪些是恶性的。
核心思路:用过去N根K线的订单流特征(如累积Delta、大单占比、失衡比率、POC偏移等)作为输入特征,预测未来M根K线的价格方向(分类)或价格变化幅度(回归)。
第一步:数据准备与特征工程
我们先从Tick数据开始。假设你已经有了一组逐笔成交数据,包含时间、价格、成交量、买卖方向(Bid/Ask)。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.metrics import accuracy_score, mean_absolute_error
# 模拟订单流数据
np.random.seed(42)
n = 10000
data = pd.DataFrame({
'timestamp': pd.date_range('2024-01-01', periods=n, freq='1s'),
'price': 100 + np.cumsum(np.random.randn(n) * 0.1),
'volume': np.random.randint(1, 100, n),
'side': np.random.choice(['B', 'S'], n, p=[0.5, 0.5])
})
data['delta'] = np.where(data['side'] == 'B', data['volume'], -data['volume'])
print(data.head())
嗯,这里要注意:真实数据中买卖方向需要从交易所的逐笔数据中解析,有些平台会直接提供Delta字段。我建议你至少积累1个月以上的数据,样本量太少模型容易过拟合。
构建特征矩阵
我们需要把原始Tick数据聚合到K线级别,然后计算订单流特征。下面这个函数是我项目中常用的,你直接复用就行:
def build_orderflow_features(df, freq='5min'):
# 聚合K线
ohlc = df.resample(freq, on='timestamp').agg({
'price': 'ohlc',
'volume': 'sum',
'delta': 'sum'
})
ohlc.columns = ['open', 'high', 'low', 'close', 'volume', 'delta']
ohlc.dropna(inplace=True)
# 特征1:累积Delta
ohlc['cum_delta'] = ohlc['delta'].cumsum()
# 特征2:Delta与成交量的比率(衡量资金强度)
ohlc['delta_ratio'] = ohlc['delta'] / (ohlc['volume'] + 1e-8)
# 特征3:大单占比(假设单笔成交量>50为大单)
large_trades = df[df['volume'] > 50].resample(freq, on='timestamp')['volume'].sum()
ohlc['large_trade_ratio'] = large_trades / (ohlc['volume'] + 1e-8)
# 特征4:价格变化率
ohlc['ret'] = ohlc['close'].pct_change()
# 特征5:波动率(用高低价差衡量)
ohlc['volatility'] = (ohlc['high'] - ohlc['low']) / ohlc['close']
# 特征6:POC偏移(假设POC是当根K线成交量最大的价格,这里简化用close代替)
ohlc['poc_offset'] = (ohlc['close'] - ohlc['open']) / ohlc['open']
return ohlc.dropna()
features = build_orderflow_features(data)
print(features.tail())
经验之谈:特征不是越多越好。我曾经一口气加了30多个特征,结果模型训练慢,还容易过拟合。建议先选5-8个核心特征,比如Delta、大单占比、失衡比率、POC偏移、波动率。这些特征在订单流分析里已经被证明是有效的。
第二步:构建标签(分类与回归)
分类任务:预测未来1根K线的价格方向(上涨/下跌/横盘)。回归任务:预测未来1根K线的价格变化幅度。
# 分类标签:未来1根K线涨跌
features['target_cls'] = np.where(features['ret'].shift(-1) > 0.001, 1,
np.where(features['ret'].shift(-1) < -0.001, -1, 0))
# 回归标签:未来1根K线的收益率
features['target_reg'] = features['ret'].shift(-1)
# 去掉最后一行(没有未来数据)
features = features.dropna()
print(features[['ret', 'target_cls', 'target_reg']].head())
这里我设了一个阈值0.1%来区分横盘。你可以根据品种的波动率调整。比如比特币波动大,阈值可以设到0.5%。
第三步:训练分类模型
我个人比较喜欢用随机森林做基线。它不需要太多调参,对异常值也鲁棒。先试试分类:
# 准备特征和标签
feature_cols = ['cum_delta', 'delta_ratio', 'large_trade_ratio', 'ret', 'volatility', 'poc_offset']
X = features[feature_cols]
y_cls = features['target_cls']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y_cls, test_size=0.2, shuffle=False)
# 训练随机森林分类器
clf = RandomForestClassifier(n_estimators=200, max_depth=10, random_state=42)
clf.fit(X_train, y_train)
# 预测与评估
y_pred = clf.predict(X_test)
print(f"分类准确率: {accuracy_score(y_test, y_pred):.3f}")
# 特征重要性
importance = pd.DataFrame({
'feature': feature_cols,
'importance': clf.feature_importances_
}).sort_values('importance', ascending=False)
print(importance)
跑完之后你会发现,delta_ratio和large_trade_ratio往往排在前两位。这符合直觉——资金强度和大单行为对短期价格影响最大。
避坑指南:千万不要用shuffle=True!时间序列数据一旦打乱顺序,模型会学到未来信息,导致回测成绩虚高。我曾经犯过这个错误,实盘亏得很惨。记住:时间序列必须按时间顺序划分训练集和测试集。
第四步:训练回归模型
分类模型只能告诉你方向,但无法告诉你幅度。如果你想做仓位管理,回归模型更有用:
# 回归任务
y_reg = features['target_reg']
X_train_r, X_test_r, y_train_r, y_test_r = train_test_split(X, y_reg, test_size=0.2, shuffle=False)
reg = RandomForestRegressor(n_estimators=200, max_depth=10, random_state=42)
reg.fit(X_train_r, y_train_r)
y_pred_r = reg.predict(X_test_r)
mae = mean_absolute_error(y_test_r, y_pred_r)
print(f"回归MAE: {mae:.5f}")
# 看看预测值与真实值的散点图
import matplotlib.pyplot as plt
plt.scatter(y_test_r[:200], y_pred_r[:200], alpha=0.5)
plt.xlabel('真实收益率')
plt.ylabel('预测收益率')
plt.title('回归模型预测效果')
plt.show()
回归模型的MAE大概在0.002左右,意味着平均预测误差约0.2%。对于5分钟K线来说,这个精度已经可以用于仓位计算了。
第五步:模型融合与策略落地
我建议你把分类和回归模型结合起来用:
- 分类模型给出方向信号(做多/做空/观望)
- 回归模型给出预期收益率,用于计算仓位大小
- 当分类信号与回归预测方向一致时,才开仓
举个例子:分类模型预测上涨(1),回归模型预测收益率为+0.3%,那么你可以开多单,仓位大小与预测收益率成正比。如果分类预测上涨但回归预测收益率为负,说明模型之间存在分歧,最好观望。
核心逻辑:分类模型负责「做什么方向」,回归模型负责「做多少仓位」。两者互相验证,能有效降低假信号。
知识体系总览
下面这张图概括了本章的核心流程,从原始Tick数据到最终策略信号:
几点补充建议
最后,我想分享几个实战中容易踩的坑:
- 特征滞后性:订单流特征对价格的反应非常快。如果你用当前K线的特征预测同一根K线的价格变化,那是未来函数。一定要用过去K线的特征预测未来K线的标签。
- 模型更新频率:市场结构会变。我建议每周重新训练一次模型,用滚动窗口的方式。比如用过去30天的数据训练,预测未来1天。
- 不要过度依赖准确率:分类准确率60%看起来不高,但如果你的盈亏比能做到2:1,长期下来收益非常可观。关注夏普比率和最大回撤,比关注准确率更有意义。
- 试试其他模型:随机森林只是起点。你可以试试XGBoost、LSTM,甚至Transformer。但记住:复杂模型不一定更好。我在项目中用XGBoost替换随机森林后,准确率只提升了1.2%,但训练时间增加了10倍。
好了,这一章的内容就到这里。代码你拿回去跑一跑,看看自己的数据上效果如何。记住,订单流数据是金矿,机器学习是挖掘机,两者结合才能挖出真正的价值。