title: “mootdx Python 个股分析实战:从数据获取到技术指标计算”
tags:

  • Python
  • mootdx
  • 股票分析
  • 量化投资
  • 技术指标
    categories:
  • Python
    description: “用 Python + mootdx 免费获取 A 股个股行情数据,手把手实现 K 线获取、MACD/KDJ/RSI/BOLL 技术指标计算、均线金叉死叉信号检测,附完整可运行代码。”

mootdx Python 个股分析实战:从数据获取到技术指标计算

导读:这篇文章记录我用 mootdx 做 A 股个股分析的完整流程——从安装配置到拉数据,再到计算 MACD、KDJ、RSI、布林带这些常用技术指标。所有代码都可以直接复制运行,适合想用 Python 做股票分析但不想花钱买数据接口的同学。


一、为什么选 mootdx

做个股分析首先要解决数据来源问题。我试过几个方案:

  • tushare:免费额度越来越少,很多接口要积分,新用户基本不够用
  • akshare:接口多但文档比较乱,部分数据源不稳定
  • baostock:免费好用,但只支持日线以上周期,分钟级数据拿不到
  • mootdx:基于通达信协议,免费、实时、支持分钟线,还支持本地离线数据

mootdx 的最新版本是 0.11.7(2024 年 5 月发布),底层依赖 tdxpy,通过封装通达信 TCP 协议直接从通达信服务器拿数据。速度快、免费、数据准,日常个股分析绰绰有余。

⚠️ 版本说明:本文所有代码基于 mootdx 0.11.7,Python 3.8+。老版本(0.10.x 及之前)部分 API 参数有差异,建议升级到最新版。


二、安装和初始化

2.1 安装

pip install -U 'mootdx[all]'

[all] 会把所有可选依赖都装上,省得后面少这个缺那个。如果你网络环境一般,也可以只装核心:

pip install mootdx

2.2 选最快的服务器

mootdx 连的是通达信行情服务器,国内有几十个节点。第一次用建议先跑一下 bestip 测试:

python -m mootdx bestip -vv

加上 -vv 可以看到每个服务器的延迟。测完之后会自动写入配置文件,后面连接就用最快的那个节点。

2.3 验证安装

import mootdx
print(f"mootdx 版本: {mootdx.__version__}")

输出 0.11.7 就说明安装没问题。


三、获取个股行情数据

mootdx 有两种数据获取方式:在线行情(从通达信服务器拉)和本地数据(读通达信客户端的离线文件)。日常用在线行情就够了。

3.1 创建行情客户端

from mootdx.quotes import Quotes

client = Quotes.factory(market='std', bestip=True, heartbeat=True)

几个参数说明:

参数 说明
market='std' 标准市场(A股)。期货期权用 'ext'
bestip=True 自动选择最快服务器
heartbeat=True 保持心跳,长时间运行不会断连
multithread=True 多线程模式,批量查询时开启

3.2 获取 K 线数据

client.bars() 是获取 K 线的主力方法:

df = client.bars(symbol='600519', frequency=9, offset=120)
print(df.tail(5))

frequency 参数控制 K 线周期:

frequency 值 周期
9 日线
5 5 分钟线
6 15 分钟线
7 30 分钟线
8 60 分钟线
10 周线
11 月线

offset 是返回的数据条数,单次最多约 800 条。需要更多数据就得分批拉取。

返回的 DataFrame 列名如下:

open      开盘价
close     收盘价
high      最高价
low       最低价
vol       成交量(手)
amount    成交额

3.3 获取实时报价

quote = client.quotes(symbol='600519')
print(quote)

返回当前最新的买卖盘口、现价、涨跌幅等数据。symbol 也可以传列表,一次查多只:

quotes = client.quotes(symbol=['600519', '000858', '300750'])

3.4 获取分时数据

minute_data = client.minute(symbol='600519')

返回当天的分时走势数据。

💡 我的习惯:日常分析用日线 frequency=9 + offset=120 拿最近半年数据。做短线的时候切换到 5 分钟线 frequency=5


四、计算技术指标

拿到数据之后,就可以开始算技术指标了。我这里不用 TA-Lib(安装太折腾),直接用 pandas 和 numpy 手写,逻辑更清晰。

4.1 封装一个数据获取函数

先把数据拉取和预处理封装起来,后面所有指标计算都基于这个函数:

import pandas as pd
import numpy as np
from mootdx.quotes import Quotes

client = Quotes.factory(market='std', bestip=True, heartbeat=True)

def get_stock_data(symbol, days=120):
    df = client.bars(symbol=symbol, frequency=9, offset=days)
    if df is None or df.empty:
        print(f"未获取到 {symbol} 的数据")
        return None
    df = df.copy()
    df.index = range(len(df))
    return df

df = get_stock_data('600519', 120)
print(df.tail(3))

4.2 均线(MA)

均线是最基础的趋势指标,5 日线和 20 日线的金叉死叉是很多人看趋势的第一步:

def calc_ma(df, periods=[5, 10, 20, 60]):
    for p in periods:
        df[f'MA{p}'] = df['close'].rolling(window=p).mean()
    return df

df = calc_ma(df)
print(df[['close', 'MA5', 'MA20']].tail(5))

检测金叉死叉信号:

def detect_cross(df):
    df['MA5_prev'] = df['MA5'].shift(1)
    df['MA20_prev'] = df['MA20'].shift(1)
    df['golden_cross'] = (df['MA5_prev'] <= df['MA20_prev']) & (df['MA5'] > df['MA20'])
    df['death_cross'] = (df['MA5_prev'] >= df['MA20_prev']) & (df['MA5'] < df['MA20'])
    return df

df = detect_cross(df)
crosses = df[df['golden_cross'] | df['death_cross']]
print(f"最近一次金叉/死叉: {crosses[['close', 'golden_cross', 'death_cross']].tail(1)}")

4.3 MACD

MACD 是趋势类指标里用得最多的,参数一般用默认的 12/26/9:

def calc_macd(df, fast=12, slow=26, signal=9):
    ema_fast = df['close'].ewm(span=fast, adjust=False).mean()
    ema_slow = df['close'].ewm(span=slow, adjust=False).mean()
    df['DIF'] = ema_fast - ema_slow
    df['DEA'] = df['DIF'].ewm(span=signal, adjust=False).mean()
    df['MACD'] = 2 * (df['DIF'] - df['DEA'])
    return df

df = calc_macd(df)
print(df[['close', 'DIF', 'DEA', 'MACD']].tail(5))

检测 MACD 金叉(DIF 上穿 DEA):

df['DIF_prev'] = df['DIF'].shift(1)
df['DEA_prev'] = df['DEA'].shift(1)
df['macd_cross'] = (df['DIF_prev'] <= df['DEA_prev']) & (df['DIF'] > df['DEA'])

macd_signals = df[df['macd_cross']]
if not macd_signals.empty:
    print(f"MACD 最近一次金叉在倒数第 {len(df) - macd_signals.index[-1]} 根K线")
else:
    print("近 120 个交易日内无 MACD 金叉")

4.4 KDJ

KDJ 是震荡类指标,短线交易者用得很多。参数通常用 9/3/3:

def calc_kdj(df, n=9, m1=3, m2=3):
    low_min = df['low'].rolling(window=n).min()
    high_max = df['high'].rolling(window=n).max()
    rsv = (df['close'] - low_min) / (high_max - low_min) * 100

    K = np.zeros(len(df))
    D = np.zeros(len(df))
    K[0] = 50
    D[0] = 50
    for i in range(1, len(df)):
        K[i] = (2/3) * K[i-1] + (1/3) * rsv.iloc[i]
        D[i] = (2/3) * D[i-1] + (1/3) * K[i]
    df['K'] = K
    df['D'] = D
    df['J'] = 3 * K - 2 * D
    return df

df = calc_kdj(df)
print(df[['close', 'K', 'D', 'J']].tail(5))

判断超买超卖:

last = df.iloc[-1]
if last['K'] > 80 and last['D'] > 80:
    print("KDJ 超买区域,注意回调风险")
elif last['K'] < 20 and last['D'] < 20:
    print("KDJ 超卖区域,关注反弹机会")
else:
    print(f"KDJ 当前: K={last['K']:.1f}, D={last['D']:.1f}, J={last['J']:.1f}")

4.5 RSI

RSI(相对强弱指标)也是判断超买超卖的经典指标:

def calc_rsi(df, periods=[6, 12, 24]):
    for period in periods:
        delta = df['close'].diff()
        gain = delta.where(delta > 0, 0)
        loss = (-delta).where(delta < 0, 0)
        avg_gain = gain.rolling(window=period).mean()
        avg_loss = loss.rolling(window=period).mean()
        rs = avg_gain / avg_loss
        df[f'RSI{period}'] = 100 - (100 / (1 + rs))
    return df

df = calc_rsi(df)
print(df[['close', 'RSI6', 'RSI12', 'RSI24']].tail(5))

RSI 的判断逻辑:

RSI 值 含义
> 70 超买,短期可能回调
30-70 正常区间
< 30 超卖,短期可能反弹

4.6 布林带(BOLL)

布林带用来判断波动率和支撑压力位:

def calc_boll(df, n=20, k=2):
    df['BOLL_MID'] = df['close'].rolling(window=n).mean()
    std = df['close'].rolling(window=n).std()
    df['BOLL_UP'] = df['BOLL_MID'] + k * std
    df['BOLL_DN'] = df['BOLL_MID'] - k * std
    return df

df = calc_boll(df)
print(df[['close', 'BOLL_UP', 'BOLL_MID', 'BOLL_DN']].tail(5))

价格突破上轨可能要回调,跌破下轨可能要反弹。但实际交易中布林带的用法远比这个复杂,不能简单照搬。


五、整合成一个分析函数

把上面的指标计算整合到一起,输入股票代码直接出分析报告:

def analyze_stock(symbol, days=120):
    df = get_stock_data(symbol, days)
    if df is None:
        return

    df = calc_ma(df)
    df = calc_macd(df)
    df = calc_kdj(df)
    df = calc_rsi(df)
    df = calc_boll(df)

    last = df.iloc[-1]
    prev = df.iloc[-2]

    print(f"{'='*50}")
    print(f"个股分析: {symbol}")
    print(f"最新收盘价: {last['close']:.2f}")
    print(f"涨跌幅: {(last['close'] - prev['close']) / prev['close'] * 100:.2f}%")
    print(f"{'='*50}")

    print(f"\n【均线系统】")
    print(f"  MA5={last['MA5']:.2f}  MA10={last['MA10']:.2f}  MA20={last['MA20']:.2f}  MA60={last['MA60']:.2f}")
    if last['close'] > last['MA20']:
        print(f"  收盘价站上20日均线,中期趋势偏多")
    else:
        print(f"  收盘价跌破20日均线,中期趋势偏弱")

    print(f"\n【MACD】")
    print(f"  DIF={last['DIF']:.3f}  DEA={last['DEA']:.3f}  MACD={last['MACD']:.3f}")
    if last['MACD'] > 0:
        print(f"  MACD 柱状线为正,多头动能")
    else:
        print(f"  MACD 柱状线为负,空头动能")

    print(f"\n【KDJ】")
    print(f"  K={last['K']:.1f}  D={last['D']:.1f}  J={last['J']:.1f}")
    if last['K'] > 80 and last['D'] > 80:
        print(f"  ⚠️ 超买区域,注意回调风险")
    elif last['K'] < 20 and last['D'] < 20:
        print(f"  ⚠️ 超卖区域,关注反弹机会")

    print(f"\n【RSI】")
    print(f"  RSI6={last['RSI6']:.1f}  RSI12={last['RSI12']:.1f}  RSI24={last['RSI24']:.1f}")

    print(f"\n【布林带】")
    print(f"  上轨={last['BOLL_UP']:.2f}  中轨={last['BOLL_MID']:.2f}  下轨={last['BOLL_DN']:.2f}")
    if last['close'] > last['BOLL_UP']:
        print(f"  ⚠️ 价格突破上轨,短期可能超买")
    elif last['close'] < last['BOLL_DN']:
        print(f"  ⚠️ 价格跌破下轨,短期可能超卖")

    return df

df = analyze_stock('600519')

运行结果大概长这样:

==================================================
个股分析: 600519
最新收盘价: 1756.00
涨跌幅: -0.85%
==================================================

【均线系统】
  MA5=1762.40  MA10=1768.30  MA20=1755.60  MA60=1720.50
  收盘价站上20日均线,中期趋势偏多

【MACD】
  DIF=5.230  DEA=8.120  MACD=-5.780
  MACD 柱状线为负,空头动能

【KDJ】
  K=65.3  D=72.1  J=51.7

【RSI】
  RSI6=42.3  RSI12=48.7  RSI24=55.1

【布林带】
  上轨=1810.50  中轨=1755.60  下轨=1700.70

六、批量筛选:找均线多头排列的股票

单个分析看完了,再举一个批量筛选的例子——从自选股列表里找出均线多头排列(MA5 > MA10 > MA20 > MA60)的股票:

def screen_bullish(stock_list):
    results = []
    for symbol in stock_list:
        try:
            df = get_stock_data(symbol, 120)
            if df is None or len(df) < 60:
                continue
            df = calc_ma(df)
            last = df.iloc[-1]
            if last['MA5'] > last['MA10'] > last['MA20'] > last['MA60']:
                results.append({
                    '代码': symbol,
                    '收盘价': last['close'],
                    'MA5': round(last['MA5'], 2),
                    'MA20': round(last['MA20'], 2),
                    'MA60': round(last['MA60'], 2),
                })
        except Exception as e:
            print(f"{symbol} 查询失败: {e}")
            continue
    return pd.DataFrame(results)

watchlist = ['600519', '000858', '300750', '601318', '000001', '600036']
result = screen_bullish(watchlist)
print(result)

⚠️ 注意:批量查询时建议加个 time.sleep(0.5) 做间隔,请求太频繁可能被服务器断连。mootdx 有断线重连机制,但控频总归是更稳妥的做法。


七、踩过的坑

1. 返回数据是 DataFrame 不是字典

我刚开始用的时候老把 client.bars() 的返回值当字典处理,结果各种报错。mootdx 返回的是 pandas DataFrame,得用 DataFrame 的方式操作。

2. frequency 参数不是字面意思

frequency=9 是日线,不是 9 天。这个编码是通达信协议的历史遗留,mootdx 只是沿用了。具体的对应关系上面第三节的表格里有。

3. 单次数据量有上限

offset 最大约 800 条。如果你要拉几年的日线数据,得分批请求。分钟线的数据量更大,更要注意这个限制。

4. 本地数据需要通达信客户端

如果你用 Reader 读取本地数据,前提是电脑上装了通达信客户端,而且数据目录要配置对。Windows 一般在 C:/new_tdx/vipdoc,macOS 在 /Applications/通达信.app/Contents/VIPDOC


八、速查表

操作 代码
安装 pip install -U 'mootdx[all]'
测速选服务器 python -m mootdx bestip -vv
创建客户端 Quotes.factory(market='std', bestip=True)
日 K 线 client.bars(symbol='600519', frequency=9, offset=120)
5 分钟线 client.bars(symbol='600519', frequency=5, offset=100)
实时报价 client.quotes(symbol='600519')
分时数据 client.minute(symbol='600519')
周线 client.bars(symbol='600519', frequency=10, offset=52)
指数 K 线 client.index(symbol='000001', frequency=9)
读取本地日线 Reader.factory(market='std', tdxdir='路径').daily(symbol)
关闭连接 client.close()

九、常见问题

Q:macOS 上安装报错怎么办?

M1/M2 芯片的 Mac 试试用 Rosetta 模式安装:arch -x86_64 pip install mootdx。如果报 mini_racer 相关错误,单独装一下:pip install mini-racer

Q:连接超时怎么处理?

先跑 python -m mootdx bestip -vv 重新测速。如果所有服务器都超时,检查网络环境——公司网络可能屏蔽了通达信的端口。

Q:返回的数据和通达信客户端看到的不一致?

确认 frequency 参数是否正确。另外通达信的数据分前复权和后复权,mootdx 默认返回不复权数据,做回测时需要自己处理复权。

Q:可以用来做自动交易吗?

mootdx 只提供数据读取,没有交易接口。自动交易需要对接券商的 API,这是另一套东西了。

Q:和 tushare 比哪个好?

各有优劣。tushare 的财务数据更全(但需要积分),mootdx 的行情数据更快更稳定。我的做法是行情数据用 mootdx,财务数据用 akshare。


参考链接

  • mootdx 官方文档:https://mootdx.readthedocs.io/
  • mootdx GitHub 仓库:https://github.com/mootdx/mootdx
  • mootdx PyPI 页面:https://pypi.org/project/mootdx/
  • MyTT 技术指标库(通达信指标 Python 移植):https://github.com/mpquant/MyTT

写了这么多,如果对你有帮助的话,给我点个赞 👍 收个藏 📌 吧~

如果你在用 Python 做股票分析的过程中遇到了什么问题,或者有更好的数据处理方案,欢迎在评论区聊聊。量化这条路坑挺多的,大家多交流才能少踩坑。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐