A股「早选晚打」双引擎:用 Ollama 本地推理 + DeepSeek 云端精筛搭建零成本量化流水线

A股「早选晚打」双引擎:用 Ollama 本地推理 + DeepSeek 云端精筛搭建零成本量化流水线

国内做 AI + 量化的都知道:东方财富 API 被 DPI 拦截、AKShare 函数名随版本漂移、策略从设计到落地中间隔着无数坑。本文分享一套真实跑通的「早选晚打」双引擎交易系统——Ollama 本地粗筛 + DeepSeek 精筛 + 腾讯财经零成本数据源,每天 2.5 分钟出候选池,14:30 尾盘出触发信号。


一、问题:策略再好,调不通等于零

上一篇文章解决了数据源的问题(绕过 DPI、零成本获取全 A 股行情)。但如果只是把数据喂给 LLM 问「哪只股票值得买」,结果就是:

# ❌ 天真方案
prompt = f"分析这 4300 只股票,推荐 3 只"
response = deepseek.chat(prompt)
# 返回:context length exceeded(4300 行的 JSON 远超上下文窗口)
# 或者:推荐了 ST 股、强周期股当价值股、代码和名称对不上

量化交易和 AI 的结合不是「把行情数据丢给 GPT」,而是一个多阶段的工程问题。


二、架构:「早选晚打」双引擎

为什么是双引擎?

去年试过「狙击埋伏」策略——预设精确买点,等市场自己送上门。效果很差:小资金绑在「等待」上,两三天没信号,心态容易崩。

反思后确定了两个原则:

  1. 主动出击优于被动等待——不等击球点,看到止跌信号就入场
  2. 两道防线互补——激进突破 + 稳健回调,覆盖不同市况

最终架构如下:

09:20  策略一·量价突破  → vp_breakout_scanner.py  → 3 只短线突破候选
09:25  策略二·回调强买  → a_share_scanner.py        → 5 只回调候选池
                              [用户白天做功课、盯盘]
14:30  尾盘综合分析     → closing_analysis.py         → 入场触发信号
14:30 - 15:00  用户决策 + 下单

策略定位于 14:30 尾盘——价格全天博弈后更诚实、T+1 友好、趋势已走完。

策略二:回调强买(主线)

核心逻辑:不等股价跌到预设狙击区,看到「止跌信号」就主动入场。

三个入场触发器(满足任一即可进入候选):

触发器 条件 实现
🔫 缩量止跌 当日量 < 前日量×0.7 且收阳 盘口观察
🔫 V反信号 盘中低点距 MA20 ≤ 1% 且反弹 ≥ 1% closing_analysis.py 自动检测
🔫 均线企稳 盘中触及 MA20 后反弹企稳 自动检测

止损/止盈:

  • 止损 = MA20 × 0.98(以均线为锚,非固定百分比)
  • 止盈 = min(入场价 × 1.08, 前期高点)
  • 持有 3-5 天

策略一:量价突破(辅线)

不猜顶底,只在资金已经动手时跟进:

  • 昨日成交额 > 20日均量 × 1.5
  • 收盘价 > 20日最高价的 95%
  • MA20 向上
  • PE 5-40,市值 > 50亿

止损/止盈:-3% / +5%,持有 1-3 天。


三、数据管道:用腾讯财经 API 零成本获取全 A 股行情

东方财富 API 被 DPI 拦截(TCP 通但 HTTP 层 RST),新浪财经同理。但 qt.gtimg.cn 不受拦截,50只/批,全量 4300+ 只约 80 秒,返回 GBK 编码。

import requests

def fetch_batch(codes: list[str]) -> list[dict]:
    """腾讯财经批量查询"""
    url = "http://qt.gtimg.cn/q=" + ",".join(codes)
    resp = requests.get(url, timeout=10)
    resp.encoding = "gbk"  # ⚠️ 必须设 GBK
    results = []
    for line in resp.text.strip().split("\n"):
        if '="' not in line:
            continue
        parts = line.split("~")
        results.append({
            "code": parts[2],
            "name": parts[1],
            "price": float(parts[3]),
            "pe": float(parts[39]) if parts[39] else 0,
            "amount": float(parts[36]) * 10000 if parts[36] else 0,
            "ret_5d": float(parts[62]) if parts[62] else 0,
            "ret_10d": float(parts[63]) if parts[63] else 0,
            "ret_20d": float(parts[64]) if parts[64] else 0,
        })
    return results

关键字段映射(腾讯 API 零文档,全靠逆向):

parts 索引 含义 用途
parts[62] 5日涨跌% 计算 MA5 / 短期动量
parts[63] 10日涨跌% 计算 MA10,趋势健康度核心输入
parts[64] 20日涨跌% 计算 MA20 / 狙击区 / 止损
parts[35] 价/量/额 一字拆三字段
parts[47-48] 52周高低 判断当前位置

ret_10d 反推 MA10:

ma10 = price / (1 + ret_10d / 100)

四、五层筛选流水线

策略二的扫描器 a_share_scanner.py 包含完整的 L0-L5 流水线:

全市场 4,300+ (腾讯 API, ~80s)
  │
  ├─ L1: 狙击初筛
  │   ST 过滤 / 京交所排除 / 价格≤3 / PE≤0 或 >50 / 市值<50亿
  │   距 MA20 距离排序 → Top 50
  │
  ├─ L2: 趋势健康度(4分制,≥3 才进候选)
  │   ① MA5 > MA10 > MA20 (多头排列)
  │   ② 股价 > MA20 (中期趋势)
  │   ③ 20日涨幅 > -10% (非急速下跌)
  │   ④ 股价 > 52周低 × 1.05 (有安全垫)
  │
  ├─ L3: PSS 五维加权评分
  │   入场精度 30% + 风报比 25% + 趋势 20% + 基本面 15% + 催化剂 10%
  │
  ├─ L4: Ollama 本地粗筛 (deepseek-r1:14b, ~22s)
  │   Top 15 → 金子 vs 石块判断
  │
  └─ L5: DeepSeek 云端精筛 (deepseek-chat, ~19s)
      逐条检查 6 条铁律 → Top 3-5 推荐

总耗时:约 150 秒(2.5 分钟)

L4: Ollama 本地粗筛

用 RTX 5080 (16GB) 跑 deepseek-r1:14b,推理速度 ~88.7 tok/s。

import requests

OLLAMA_URL = "http://localhost:11434/api/generate"

json_schema = {
    "type": "object",
    "properties": {
        "verified": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "code": {"type": "string"},
                    "name": {"type": "string"},
                    "verdict": {"type": "string", "enum": ["gold", "rock"]},
                    "pss_ok": {"type": "boolean"},
                    "fatal_flaw": {"type": "string"},
                },
                "required": ["code", "name", "verdict"],
            }
        }
    },
    "required": ["verified"],
}

resp = requests.post(OLLAMA_URL, json={
    "model": "deepseek-r1:14b",
    "prompt": prompt,
    "format": json_schema,   # Schema 约束输出
    "stream": False,
    "options": {"num_ctx": 4096, "temperature": 0.05}
})

关键参数temperature=0.05(不是 0.3 也不是 0.15)。经过实测对比,0.05 覆盖面更广(13 vs 9 只),且输出稳定可复现——同一天同一输入两次跑出来的 Top 5 重叠率 100%。

L5: DeepSeek 云端精筛

Prompt 内嵌 6 条交易铁律:

❶ 公告排雷: 搜索「代码 减持 质押 监管函」→ 一票否决
❷ 强周期判断: 铝/钢/煤/化工等 PE 低 ≠ 低估
❸ 下跌加速: 等止跌信号,别接飞刀
❹ 仓位 ≤ 10%(小资金测试阶段)
❺ 目标价 > 成本价
❻ 止损必须同步给出

五、风控体系:三层否决 + 6 条铁律

年初踩过两次坑后(联创光电大股东减持+82%质押、宏桥控股强周期+下跌加速),重建了三层否决:

L1 量化否决
  ├─ 强周期行业 (22个关键词) + trend<2 → 一票否决
  └─ 下跌加速 (5日跌幅 > 20日跌幅×0.5) + trend≤1 → 一票否决

L2 公告排雷
  └─ AnySearch 批量扫描 Top 15
     命中「减持/质押/监管函/立案」→ 一票否决

L3 AI 铁律兜底
  └─ DeepSeek 精筛逐条检查 6 条铁律

强周期行业关键词表:

CYCLICAL_KEYWORDS = [
    "铝", "钢", "铁", "煤", "炭", "化工", "有色", "水泥", "玻璃",
    "铜", "锌", "镍", "锂", "钴", "稀土", "石化", "原油", "天然气",
    "航运", "港口", "造船", "造纸", "化纤", "化肥", "农药",
]

六、部署:4 条 Cron 实现全自动

cronjob create --name "策略二·回调强买"  --script "sniper_scan.sh"  --schedule "25 9 * * 1-5"
cronjob create --name "策略一·量价突破"  --script "vp_breakout_scanner.py" --no-agent --schedule "20 9 * * 1-5"
cronjob create --name "尾盘综合分析"      --script "closing_analysis.py" --no-agent --schedule "30 14 * * 1-5"
cronjob create --name "P3情报融合日报"    --script "p3_fusion_briefing.py" --no-agent --schedule "0 17 * * 1-5"

Cron 踩坑记

  • python 必须加 -u 强制无缓冲,否则 cron 环境下 stdout 全被吞
  • 脚本路径必须物理位于 ~/.hermes/scripts/,软链接也不行
  • .env 加载必须用 load_dotenv(override=True),否则 cron 进程已有的环境变量会遮蔽 .env 里的新 key

七、实战踩坑:那些让你怀疑人生的 bug

1. ak.stock_info_a_code_name() 超时 7 分钟

AKShare 的 stock_info_a_code_name() 会连 www.bse.cn(北京证券交易所),在本机网络下频繁超时(7 分钟+),导致整个扫描流水线不可用。

# ❌ 错误:连 BSE 超时 7 分钟
df = ak.stock_info_a_code_name()

# ✅ 正确:分别获取沪深代码,完全避开 BSE
sh = ak.stock_info_sh_name_code()
sz = ak.stock_info_sz_name_code()
# 列名归一化
sh = sh.rename(columns={"证券代码": "code", "证券简称": "name"})
sz = sz.rename(columns={"A股代码": "code", "A股简称": "name"})

2. Ollama 名称幻觉

Ollama 在排名股票时经常把代码和名称搞混——输出 "600030 招商银行",但 600030 是中信证券。

修复:解析 Ollama 输出的 JSON 后,用源数据逐条校正名称。

for stock in results:
    code_clean = "".join(filter(str.isdigit, str(stock["code"])))
    match = df[df["symbol_clean"] == code_clean]
    if not match.empty:
        stock["name"] = match.iloc[0]["name"]  # 强制覆盖

3. DeepSeek API 模型名陷阱

deepseek-v4-flash 返回空内容(HTTP 200 但 content 为空字符串)。改用 deepseek-chat 正常——虽然后端映射到同一模型,但响应处理不同。

4. 腾讯 API GBK 编码

qt.gtimg.cn 返回 GBK 编码,不设 resp.encoding = "gbk" 直接中文乱码——Python requests 默认猜编码会猜错。


八、总结:本地算力 + 云端精筛 = 零成本量化流水线

这条流水线的核心思想是 用本地 Ollama 做粗活,用 DeepSeek API 做细活

阶段 工具 耗时 成本
数据获取 腾讯 API (qt.gtimg.cn) 80s 免费
量化初筛 pandas 多因子 <1s 本地
公告排雷 AnySearch API 30s 免费
AI 粗筛 Ollama deepseek-r1:14b 22s 本地 GPU
AI 精筛 DeepSeek deepseek-chat 19s ~¥0.01/次
总计 ~150s ≈ 免费

每天早晨 09:25 产出候选池,下午 14:30 产出触发信号,全程无需盯盘。

下一步:接入东方财富财富号社区情绪数据(已经找到 API)、加入商品期货→A股映射(螺纹钢→钢铁股、碳酸锂→锂电股),构建信息领先优势。


全套代码开源:a_share_scanner.py + closing_analysis.py + fetch_a_shares.py
GitHub: jipin-ai/ji_pin(a-stock-intel 工具包)
本文是 CSDN 技术阵地第二篇,前一篇:绕过 DPI、零成本搭建 A 股智能 Agent 工具9)

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐