Featured image of post 用 DuckDB 搭建自动化金融日报:从数据到变现的完整路径

用 DuckDB 搭建自动化金融日报:从数据到变现的完整路径

手把手教你用 DuckDB + Python 搭建全自动金融日报系统,从数据采集、SQL 分析到 Telegram 推送,全流程零运维,让数据分析师通过信息差实现每月额外 3000+ 收入。

在数据变现的赛道上,信息加工能力是最被低估的技能之一。大多数数据分析师会把报告写作局限在内部汇报里,却不知道这套能力可以直接转化为订阅制收入。今天我们来拆解一个真实的变现产品:用 DuckDB 搭建全自动金融日报,3 小时上线,每月稳定额外收入 3000+ 元。

架构图

一、产品逻辑:你在卖什么?

先搞清楚定价模型,再设计技术架构。一个典型的金融日报产品:

  • 目标客户:小型投资机构、独立理财顾问、交易社群成员
  • 交付物:每天早间推送到 Telegram / 微信群的简报
  • 内容结构:大盘概览 → 热点板块 → 资金流向 → 风险提示
  • 定价参考:月度订阅 99-299 元,年费 999 元

核心价值不是"数据"——公开数据到处都是。你卖的是加工后的洞察。Raw data 免费获取,洞察服务收费。

二、技术架构:为什么选 DuckDB?

传统方案会用 PostgreSQL + Python 脚本,但这对个人开发者来说运维成本太高。DuckDB 的核心优势:

维度传统方案(PostgreSQL)DuckDB 方案
部署成本需要数据库服务器单文件,零部署
运维复杂度备份、扩容、权限管理无运维,开箱即用
分析性能行式存储,聚合较慢列式存储,百万行秒级聚合
数据接入需先 ETL 入库原生支持 HTTP / JSON / CSV
嵌入方式客户端连接内嵌库,Python/R/Node.js 无缝集成
月度成本云服务器 50-200 元完全免费

整个系统只需要一个 Python 脚本 + 一个 GitHub Actions 定时任务,月成本 = 0 元

三、项目结构

financial-daily-report/
├── report.py          # 主程序(数据拉取 → 分析 → 推送)
├── config.yaml        # 配置(API 密钥、Telegram Bot Token)
├── data/              # DuckDB 数据库文件
│   └── market.db
├── .github/workflows/
│   └── daily-cron.yml  # GitHub Actions 定时任务
└── requirements.txt

四、核心代码实现

第一步:数据拉取与存储

DuckDB 可以直接查询 HTTP 接口返回的 JSON,也可以从 CSV/Parquet 文件读取,无需先落库:

import duckdb
import httpx
import yaml
from pathlib import Path
from datetime import datetime, timedelta
import json

CONFIG_PATH = Path(__file__).parent / "config.yaml"
with open(CONFIG_PATH) as f:
    cfg = yaml.safe_load(f)

DB_PATH = Path(__file__).parent / "data" / "market.db"
DB_PATH.parent.mkdir(parents=True, exist_ok=True)

def fetch_market_data() -> dict:
    """拉取 A 股市场核心指标"""
    # 实际项目可替换为 AKShare、Tushare 等真实 API
    return {
        "indices": [
            {"code": "SH000001", "name": "上证指数", "close": 3285.67, "pct": 0.42},
            {"code": "SZ399001", "name": "深证成指", "close": 10567.23, "pct": 0.18},
            {"code": "SZ399006", "name": "创业板指", "close": 2156.89, "pct": -0.35},
        ],
        "sectors": [
            {"name": "半导体", "pct": 2.14, "volume_ratio": 1.32},
            {"name": "新能源", "pct": 1.87, "volume_ratio": 1.15},
            {"name": "白酒", "pct": -0.52, "volume_ratio": 0.88},
            {"name": "AI应用", "pct": 3.21, "volume_ratio": 1.67},
        ],
        "capital_flow": {
            "northbound": 12.5,
            "margin": -3.2,
            "main_force": 45.8,
        },
    }

def save_to_duckdb(data: dict) -> None:
    conn = duckdb.connect(str(DB_PATH))
    today = datetime.now().strftime("%Y-%m-%d")

    # 创建表(如不存在则创建),每日先删后插实现增量更新
    conn.execute("""
        CREATE TABLE IF NOT EXISTS daily_indices (
            date        DATE,
            code        VARCHAR,
            name        VARCHAR,
            close       DOUBLE,
            pct_change  DOUBLE,
            created_at  TIMESTAMP
        )
    """)
    conn.execute("DELETE FROM daily_indices WHERE date = ?", [today])
    for idx in data["indices"]:
        conn.execute(
            "INSERT INTO daily_indices VALUES (?, ?, ?, ?, ?, ?)",
            [today, idx["code"], idx["name"], idx["close"], idx["pct"], datetime.now()],
        )

    conn.execute("""
        CREATE TABLE IF NOT EXISTS daily_sectors (
            date           DATE,
            sector_name    VARCHAR,
            pct_change     DOUBLE,
            volume_ratio   DOUBLE,
            created_at     TIMESTAMP
        )
    """)
    conn.execute("DELETE FROM daily_sectors WHERE date = ?", [today])
    for sec in data["sectors"]:
        conn.execute(
            "INSERT INTO daily_sectors VALUES (?, ?, ?, ?, ?)",
            [today, sec["name"], sec["pct"], sec["volume_ratio"], datetime.now()],
        )

    conn.execute("""
        CREATE TABLE IF NOT EXISTS daily_capital (
            date           DATE,
            northbound     DOUBLE,
            margin_change  DOUBLE,
            main_force     DOUBLE,
            created_at     TIMESTAMP
        )
    """)
    conn.execute("DELETE FROM daily_capital WHERE date = ?", [today])
    conn.execute(
        "INSERT INTO daily_capital VALUES (?, ?, ?, ?, ?)",
        [today, data["capital_flow"]["northbound"],
         data["capital_flow"]["margin"], data["capital_flow"]["main_force"],
         datetime.now()],
    )

    conn.commit()
    conn.close()

第二步:SQL 驱动的智能分析引擎

这部分是产品的核心竞争力——用 SQL 完成趋势判断、板块热度和资金信号的分析:

def analyze_history(days: int = 5) -> dict:
    """基于历史数据生成趋势洞察"""
    conn = duckdb.connect(str(DB_PATH))

    # 上证近 N 日走势
    sh_trend = conn.execute("""
        SELECT date, close, pct_change
        FROM daily_indices
        WHERE code = 'SH000001'
          AND date >= CURRENT_DATE - INTERVAL ? DAY
        ORDER BY date DESC
    """, [days]).fetchall()

    # 板块热度排名(按量比筛选强势板块)
    hot_sectors = conn.execute("""
        SELECT sector_name, pct_change, volume_ratio
        FROM daily_sectors
        WHERE date = CURRENT_DATE
          AND volume_ratio > 1.0
        ORDER BY pct_change DESC
        LIMIT 3
    """).fetchall()

    # 连续 N 日资金流入统计
    capital_summary = conn.execute("""
        SELECT
            SUM(northbound)  AS total_north,
            AVG(main_force)  AS avg_main,
            CASE
                WHEN SUM(northbound) > 0 THEN '🟢 外资流入'
                ELSE '🔴 外资流出'
            END AS signal
        FROM daily_capital
        WHERE date >= CURRENT_DATE - INTERVAL ? DAY
    """, [days]).fetchone()

    conn.close()
    return {
        "sh_trend": sh_trend,
        "hot_sectors": hot_sectors,
        "capital": capital_summary,
    }

这个 SQL 分析引擎有几个关键点:

  1. CURRENT_DATE - INTERVAL ? DAY:DuckDB 支持参数化日期计算,避免字符串拼接
  2. CASE WHEN 信号生成:直接在 SQL 里做条件判断,比 Python 后处理更清晰
  3. 增量删除再插入DELETE WHERE date = ? 保证每日数据幂等性

第三步:日报文本生成

def build_report(data: dict, insight: dict) -> str:
    today = datetime.now().strftime("%m月%d日")
    trend = insight["sh_trend"]
    last_close = trend[0][1] if trend else "?"
    prev_pct = trend[1][2] if len(trend) > 1 else 0

    up_days = sum(1 for row in trend if row[2] > 0)
    trend_text = f"近{len(trend)}日涨{up_days}天" if trend else "暂无历史数据"

    sector_lines = []
    for name, pct, vr in insight["hot_sectors"]:
        sector_lines.append(f"  · {name}  +{pct:.2f}%(量比{vr:.2f})")
    sector_text = "\n".join(sector_lines) or "  暂无放量板块"

    capital = insight["capital"]
    capital_text = f"{capital[2]},近{len(trend)}日北向累计{'+' if capital[0]>0 else ''}{capital[0]:.1f}亿"

    report = f"""📊 【{today} 金融日报】

━━━ 一、大盘概览 ━━━
上证指数 {last_close:.2f}{'↑' if prev_pct>=0 else '↓'}{abs(prev_pct):.2f}%)
{trend_text}

━━━ 二、热点板块 TOP3 ━━━
{sector_text}

━━━ 三、资金流向 ━━━
{capital_text}

━━━ 四、明日提示 ━━━
{generate_signal(insight)}

📌 数据来源:公开市场数据,仅供参考"""
    return report

五、自动化部署:GitHub Actions + Cron

用 GitHub Actions 的 cron 功能实现无人值守推送,完全免费

# .github/workflows/daily-cron.yml
name: Daily Financial Report

on:
  schedule:
    - cron: '0 22 * * *'  # 北京时间 06:00(UTC 18:00)
  workflow_dispatch:  # 也支持手动触发

jobs:
  run-report:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: pip install -r requirements.txt

      - name: Run report generator
        env:
          TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }}
          TG_CHAT_ID: ${{ secrets.TG_CHAT_ID }}
        run: python report.py

      - name: Commit and push results
        run: |
          git config user.name "CI Bot"
          git config user.email "[email protected]"
          git add data/
          git commit -m "Auto update: $(date +%Y-%m-%d)" || echo "No changes"
          git push

配合 config.yaml 存储敏感配置:

# config.yaml
telegram:
  bot_token: "YOUR_BOT_TOKEN"
  chat_id: "YOUR_CHAT_ID"

data_source: "akshare"  # 或 "tushare", "mock"

六、与传统方案的效率对比

指标PostgreSQL 方案DuckDB 方案
部署时间2-4 小时(服务器+数据库)30 分钟(纯代码)
月运维成本50-200 元(云服务器)0 元
单月数据存储~150 万行(3 张表×5 年)单文件 < 5MB
查询延迟需网络连接本地毫秒级
数据恢复需备份策略.db 文件直接复制
扩展难度需垂直扩容直接追加历史数据

七、变现路径与定价建议

完成上述系统后,你的变现路径有以下几条:

  1. 订阅制日报:月度 99-299 元,年费 999 元。100 个订阅 = 月收 1-3 万。
  2. 定制化数据看板:为企业客户搭建专属监控面板,单次项目 5000-20000 元。
  3. 数据产品包:将日报升级为基础版 + 专业版,基础版免费引流,专业版付费解锁深度指标。
  4. B 端 SaaS:将系统封装为多租户 SaaS,按席位收费。

关键提醒:你的核心壁垒不是代码——代码是开源的。壁垒是数据源的稳定性洞察模型的专业度。选择一个细分领域(比如你熟悉的行业或市场),把分析逻辑做深,才是长期变现的基础。

八、扩展方向:从日报到数据产品

当你跑通了日报闭环后,可以继续扩展:

  • 多市场覆盖:A 股 → 港股 → 美股,一份代码,三份收入
  • 实时预警:加入价格突破、资金异动等实时信号推送
  • 历史回溯回测:用 DuckDB 的 Time Travel 功能回测策略
  • 可视化面板:接入 EvidenceStreamlit,自动生成 HTML 报告

📖 详细图文教程见 duckdblab.org

💡 更多 DuckDB 实战技巧 → duckdblab.org

📺 Watch video tutorials → Olap Studio YouTube

Subscribe for more DuckDB & AI automation tutorials

使用 Hugo 构建
主题 StackJimmy 设计