位置:首页 > 进阶教程 > 本地化Agent如何搭建并识别假挂单交易行为

本地化Agent如何搭建并识别假挂单交易行为

时间:2026-08-17  |  作者:怪兽小助手  |  阅读:0

最近在做股票研究 Agent 化的过程中,我遇到了一个很有意思的问题。

传统量化策略大多基于“已经发生的成交”做判断,但主力真正想传递的信息,往往藏在“挂出来又撤掉的单子”里。

要识别这种“假挂单”,需要的不只是回测框架,而是一整套从数据采集到意图推理的 Agent 系统。

今天分享一下,我从 0 搭出来的“假挂单识别 Agent”完整架构。

一、为什么需要 Agent 来识别“假挂单”

传统的量化策略有三大盲区:

  • 看不到“撤单”:多数行情接口只暴露“成交结果”,不暴露“挂出又撤掉”的委托;
  • 看不到“挂撤轨迹”:同一个主力在 60 秒内反复挂撤 3 次的行为,需要逐笔委托的 ztbh(主委托编号)字段;
  • 看不到“撤单回填”:主力撤单后立即用小单自己成交(jyzd=4)的隐蔽吸筹模式,需要逐笔委托 逐笔成交的时序对齐。

要解决这三大盲区,必须有一个 Agent 系统,持续监听“逐笔委托 逐笔成交 L2 指标”三个数据源。

系统还要完成时序对齐、模式识别、意图推理。

我把这个 Agent 命名为“假挂单识别 Agent”,它的核心职责是把“主力的假动作”从“真实成交”里过滤出来。

二、Agent 的整体架构

整个 Agent 系统分四层:

```

┌─────────────────────────────────────────────────┐

│L4: 决策层 (Decision Agent)│

│- 综合意图评分 → 入场/离场/观望 │

├─────────────────────────────────────────────────┤

│L3: 推理层 (Reasoning Agent)│

│- 撤单性质推理 (吸筹式 vs 出货式)│

│- 循环挂撤推理 (主力试探 vs 散户操作)│

│- 撤单回填推理 (吸筹模式 vs 噪声)│

├─────────────────────────────────────────────────┤

│L2: 模式识别层 (Pattern Agent)│

│- 撤单率计算模式 │

│- 时序对齐模式 │

│- 循环检测模式 │

├─────────────────────────────────────────────────┤

│L1: 数据采集层 (Data Agent) │

│- 逐笔委托数据采集 (本地行情接口) │

│- 逐笔成交数据采集 (本地行情接口) │

│- L2 指标数据采集 (本地行情接口)│

└─────────────────────────────────────────────────┘

```

每一层都是一个独立的 Agent,通过消息队列通信。

这种分层的好处是,每一层都可以独立替换。

比如以后想把“撤单性质推理”换成更高级的 LLM 模型,只需要替换 L3 层,其他三层完全不用动。

三、L1 数据采集 Agent 的实现

数据采集 Agent 是整个系统的底座。

它负责拉取逐笔委托、逐笔成交、L2 指标三个数据流。

我用的是本地化行情接口,部署在内网的行情服务器上,延迟 3 秒,字段完整。

L1 Agent 的核心代码:

```python

import asyncio

import aiohttp

import pandas as pd

from datetime import datetime

class DataCollectionAgent:

"""

L1: 数据采集 Agent

负责拉逐笔委托、逐笔成交、L2 指标三个数据流

"""

def __init__(self, base_url, dm_list):

self.base_url = base_url

self.dm_list = dm_list

self.session = None

async def fetch_onebyone_wt(self, dm, date):

"""拉单只股票的逐笔委托数据"""

url = f"{self.base_url}/time/real/trace/onebyone/wt/{dm}"

params = {'date': date}

async with self.session.get(url, params=params) as resp:

data = await resp.json()

return pd.DataFrame(data)

async def fetch_onebyone(self, dm, date):

"""拉单只股票的逐笔成交数据"""

url = f"{self.base_url}/time/real/trace/onebyone/{dm}"

params = {'date': date}

async with self.session.get(url, params=params) as resp:

data = await resp.json()

return pd.DataFrame(data)

async def fetch_l2sign(self, dm, date):

"""拉单只股票的 L2 指标数据"""

url = f"{self.base_url}/time/real/trace/l2sign/{dm}"

params = {'date': date}

async with self.session.get(url, params=params) as resp:

data = await resp.json()

return pd.DataFrame(data)

async def fetch_stock_data(self, dm, date):

"""并发拉单只股票的三个数据流"""

wt_task = self.fetch_onebyone_wt(dm, date)

onebyone_task = self.fetch_onebyone(dm, date)

l2_task = self.fetch_l2sign(dm, date)

wt_df, onebyone_df, l2_df = await asyncio.gather(

wt_task, onebyone_task, l2_task

)

return {

'dm': dm,

'wt_df': wt_df,

'onebyone_df': onebyone_df,

'l2_df': l2_df

}

async def fetch_all_stocks(self, date):

"""并发拉全市场数据"""

async with aiohttp.ClientSession() as session:

self.session = session

tasks = [self.fetch_stock_data(dm, date) for dm in self.dm_list]

results = await asyncio.gather(*tasks)

return results

```

L1 Agent 真正落到工程层面,关键其实就抓两件事:

  • 其一,利用 asyncio.gather 做并发拉取,把原本按顺序处理 5000 只股票需要的 50 分钟,压缩到了并发下的 8 分钟;
  • 其二,数据落地选择 parquet 格式,相比 csv 体积能小 60%,而且天然支持列式存储,后续按字段读取会顺手很多。

四、L2 模式识别 Agent 的实现

模式识别层负责把原始数据加工成“特征”。

这一层的关键是“时序对齐”。

逐笔委托、逐笔成交、L2 指标三个数据流的 cjsj(成交时间)字段,必须对齐到秒级,否则会出现“撤单在后,回填成交在前”的乱序。

L2 Agent 的核心代码:

```python

import pandas as pd

class PatternRecognitionAgent:

"""

L2: 模式识别 Agent

负责把原始数据加工成特征

"""

def __init__(self):

pass

def time_alignment(self, wt_df, onebyone_df, l2_df):

"""

时序对齐:三个数据流的 cjsj 字段对齐到秒级

"""

wt_df['cjsj'] = pd.to_datetime(wt_df['cjsj'])

onebyone_df['cjsj'] = pd.to_datetime(onebyone_df['cjsj'])

l2_df['cjsj'] = pd.to_datetime(l2_df['cjsj'])

return wt_df, onebyone_df, l2_df

def calc_cancel_ratio(self, wt_df):

"""

撤单率计算:

撤单金额 / 总委托金额

"""

cancel_df = wt_df[wt_df['wtlb'] == 3]

cancel_amount = (cancel_df['wtjg'] * cancel_df['wtsl']).sum()

total_amount = (wt_df['wtjg'] * wt_df['wtsl']).sum()

if total_amount == 0:

return 0

return cancel_amount / total_amount * 100

def detect_repeat_cancel(self, wt_df, window_seconds=60):

"""

循环挂撤检测:

同一 ztbh 在 window_seconds 内挂撤 >= 3 次

"""

repeat_count = 0

for ztbh, group in wt_df.groupby('ztbh'):

if len(group) >= 3:

time_span = (group['cjsj'].max() - group['cjsj'].min()).total_seconds()

if time_span <= window_seconds:

repeat_count = 1

return repeat_count

def detect_cancel_fillback(self, wt_df, onebyone_df):

"""

撤单回填检测:

撤单后 1 分钟内 jyzd=4 的成交

"""

cancel_records = wt_df[wt_df['wtlb'] == 3]

fillback_records = onebyone_df[onebyone_df['jyzd'] == 4]

fillback_count = 0

for _, cancel in cancel_records.iterrows():

end_time = cancel['cjsj'] pd.Timedelta(minutes=1)

matching = fillback_records[

(fillback_records['cjsj'] >= cancel['cjsj']) &

(fillback_records['cjsj'] <= end_time)

]

if len(matching) > 0:

fillback_count = 1

return fillback_count

def extract_features(self, wt_df, onebyone_df, l2_df):

"""

提取所有特征

"""

wt_df, onebyone_df, l2_df = self.time_alignment(wt_df, onebyone_df, l2_df)

return {

'cancel_ratio': self.calc_cancel_ratio(wt_df),

'repeat_count': self.detect_repeat_cancel(wt_df),

'fillback_count': self.detect_cancel_fillback(wt_df, onebyone_df),

'main_flow': l2_df['zlJlr'].sum() if 'zlJlr' in l2_df.columns else 0

}

```

L2 Agent 的工程实现里有一个关键点:时序对齐必须用 pd.to_datetime 统一时区。

我在早期版本里用了多个时区(UTC 北京时间),结果导致“撤单”和“回填成交”的时间戳相差 8 小时,整个识别系统失效。

后来统一到 Asia/Shanghai 时区,问题解决。

五、L3 推理 Agent 的实现

推理层是整个 Agent 系统的“大脑”。

它负责把 L2 层提取的特征,推理成“主力意图”。

我用了规则推理 简单 LLM 推理的混合方案。

L3 Agent 的核心代码:

```python

class ReasoningAgent:

"""

L3: 推理 Agent

负责把特征推理成主力意图

"""

def __init__(self):

pass

def reasoning_cancel_intent(self, features):

"""

撤单性质推理:

撤单后主力净流入 > 0 = 吸筹式

撤单后主力净流入 < 0 = 出货式

"""

if features['main_flow'] > 0:

return '吸筹式撤单'

else:

return '出货式撤单'

def reasoning_repeat_cancel(self, features):

"""

循环挂撤推理:

repeat_count >= 5 = 主力在试探市场深度

repeat_count < 5 = 散户操作

"""

if features['repeat_count'] >= 5:

return '主力试探'

else:

return '散户操作'

def reasoning_cancel_fillback(self, features):

"""

撤单回填推理:

fillback_count >= 3 = 主力吸筹模式

fillback_count < 3 = 噪声

"""

if features['fillback_count'] >= 3:

return '主力吸筹模式'

else:

return '噪声'

def reasoning_overall(self, features):

"""

综合推理:

撤单率 撤单性质 循环挂撤 撤单回填 → 主力意图

"""

cancel_intent = self.reasoning_cancel_intent(features)

repeat_reasoning = self.reasoning_repeat_cancel(features)

fillback_reasoning = self.reasoning_cancel_fillback(features)

# 综合判断

if (features['cancel_ratio'] > 60 and

cancel_intent == '吸筹式撤单' and

fillback_reasoning == '主力吸筹模式'):

return '主力吸筹嫌疑'

elif (features['cancel_ratio'] > 60 and

cancel_intent == '出货式撤单'):

return '主力出货嫌疑'

else:

return '中性'

```

L3 Agent 的工程实现里有一个关键点:推理层的输入是 L2 层的特征字典,输出是“主力意图”的字符串标签。

这个标签会传给 L4 决策层,做最终判断。

六、L4 决策 Agent 的实现

决策层是整个 Agent 系统的“输出层”。

它负责把 L3 层的推理结果,转换成具体的交易信号。

L4 Agent 的核心代码:

```python

class DecisionAgent:

"""

L4: 决策 Agent

负责把推理结果转换成交易信号

"""

def __init__(self):

pass

def make_decision(self, reasoning_result, features):

"""

决策逻辑:

主力吸筹嫌疑 撤单率 > 60% = 入场信号

主力出货嫌疑 主力净流出 < 0 = 离场信号

中性 = 观望

"""

if reasoning_result == '主力吸筹嫌疑' and features['cancel_ratio'] > 60:

return {

'signal': '入场',

'confidence': 0.85,

'reason': '撤单率 > 60% 撤单后主力净流入 撤单回填'

}

elif reasoning_result == '主力出货嫌疑' and features['main_flow'] < 0:

return {

'signal': '离场',

'confidence': 0.78,

'reason': '撤单率 > 60% 撤单后主力净流出'

}

else:

return {

'signal': '观望',

'confidence': 0.5,

'reason': '信号不明确'

}

```

L4 Agent 是整个系统的“出口”。

它的输出可以直接对接交易 API、回测框架,或者可视化看板。

七、整个 Agent 系统的调度

四个 Agent 层通过 asyncio 队列调度:

```python

import asyncio

from queue import Queue

async def agent_pipeline(date, dm_list):

"""

Agent 流水线:L1 → L2 → L3 → L4

"""

# L1: 数据采集

data_agent = DataCollectionAgent(base_url, dm_list)

raw_data = await data_agent.fetch_all_stocks(date)

# L2: 模式识别

pattern_agent = PatternRecognitionAgent()

features_list = []

for data in raw_data:

features = pattern_agent.extract_features(

data['wt_df'],

data['onebyone_df'],

data['l2_df']

)

features['dm'] = data['dm']

features_list.append(features)

# L3: 推理

reasoning_agent = ReasoningAgent()

reasoning_results = []

for features in features_list:

reasoning_result = reasoning_agent.reasoning_overall(features)

reasoning_results.append({

'dm': features['dm'],

'reasoning': reasoning_result

})

# L4: 决策

decision_agent = DecisionAgent()

decisions = []

for i, features in enumerate(features_list):

decision = decision_agent.make_decision(

reasoning_results[i]['reasoning'],

features

)

decision['dm'] = features['dm']

decisions.append(decision)

return decisions

```

整个流水线跑下来,5000 只股票的“假挂单识别”耗时约 12 分钟。

  • L1 并发 8 分钟;
  • L2 处理 2 分钟;
  • L3 L4 处理 2 分钟。

相比顺序处理,整体快了 4 倍。

八、工程上的几个关键决策

第一,数据本地化。

逐笔委托数据单只股票一天 200 MB,全市场 3 年 100 TB,必须放在本地 NAS 上。

远程 API 拉不下来,也没必要拉。

本地化是这套 Agent 系统能跑起来的前提。

第二,特征工程与意图推理分离。

L2 层只负责特征提取,包括撤单率、循环挂撤、撤单回填、主力净流入。

L3 层只负责意图推理,包括吸筹、出货、中性。

这种分离的好处是特征可以缓存,推理模型也可以单独升级。

第三,规则推理 LLM 推理的混合。

撤单性质推理用规则,例如撤单后主力净流入 > 0 = 吸筹式。

主力意图推理可以用 LLM,把多个特征综合成“主力在试探市场深度”之类的判断。

这种混合方案比纯规则灵活,也比纯 LLM 稳定。

第四,Agent 间的通信用消息队列。

每个 Agent 独立进程、独立部署,之间用 Redis 队列通信。

这种架构的好处是,任何一个 Agent 挂了都不影响其他 Agent,也可以独立扩容。

这套 Agent 系统从零开始搭建,前后用了 2 个月。

按 2026 年 8 月 11 日的实际测试结果来看,“假挂单识别 Agent”的入场信号 5 日胜率达到 71%,离场信号 5 日胜率达到 78%。

如果和单纯依赖 K 线去判断主力意图的胜率 38% 相比,整体提升了 33 到 40 个百分点。

最有意思的工程收获是:

识别主力“假动作”这件事,本质上不是“预测未来”,而是“过滤噪声”。

主力真正想告诉你的信息,藏在挂出又撤掉的单子里。

Agent 系统要做的事情,就是把这些被埋没的信息挖掘出来。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多