Python 量化入门:用 pandas 处理 A 股 K 线数据并计算技术指标

量化交易听起来很高大上,但本质就是用代码处理数据、发现规律。这篇文章从最基础的 K 线数据开始,教你用 pandas 完成数据获取、清洗、技术指标计算的全流程。不需要任何量化框架,纯 Python + pandas 就够了。


一、K 线数据长什么样?

K 线(OHLCV)是量化分析的基础数据,每一根 K 线包含:

字段 含义
open 开盘价
high 最高价
low 最低价
close 收盘价
volume 成交量
amount 成交额

一根日 K 线就是一天的价格摘要。把很多根日 K 线排起来,就是我们常说的"走势图"。

量化分析的核心工作就是对这些数据做计算,比如算均线、判断金叉死叉、计算涨跌幅排名等。


二、获取 K 线数据

获取 A 股数据有很多方式。这里用 alphafeed 做示例(也可以换成你习惯的数据源,后面的 pandas 处理逻辑完全一样):

pip install alphafeed
from alphafeed import AlphaFeed

af = AlphaFeed(api_key="your-api-key")  # 也支持环境变量 ALPHAFEED_API_KEY

# 获取贵州茅台最近 500 根日 K 线
df = af.klines.get("600519.SH", period="1d", count=500, to_dataframe=True)
print(df.shape)
print(df.columns.tolist())

拿到的 DataFrame 大概长这样:

(500, 9)
['symbol', 'timestamp', 'trade_date', 'open', 'high', 'low', 'close', 'volume', 'amount']

不管用什么数据源,只要最终拿到一个包含 open/high/low/close/volume 的 DataFrame,后面的分析流程都是通用的。


三、数据初步探索

拿到数据后,先做基本检查:

# 看前几行
print(df.head())

# 看数据类型
print(df.dtypes)

# 检查缺失值
print(df.isnull().sum())

# 基本统计
print(df[["open", "high", "low", "close", "volume"]].describe())

常见操作:把 trade_date 设为索引,方便按时间切片:

df["trade_date"] = pd.to_datetime(df["trade_date"])
df = df.set_index("trade_date").sort_index()

# 取最近一年
df_recent = df.loc["2025-06-01":]

四、计算常用技术指标

1. 涨跌幅

df["change_pct"] = df["close"].pct_change() * 100
print(df[["close", "change_pct"]].tail(10))

2. 移动平均线(MA)

均线是最基础的技术指标,用 rolling 一行搞定:

df["ma5"] = df["close"].rolling(5).mean()
df["ma10"] = df["close"].rolling(10).mean()
df["ma20"] = df["close"].rolling(20).mean()
df["ma60"] = df["close"].rolling(60).mean()

print(df[["close", "ma5", "ma10", "ma20", "ma60"]].tail(10))

怎么看均线?

  • 短期均线在长期均线上方 → 多头排列(趋势向上)
  • 短期均线从下往上穿过长期均线 → 金叉(买入信号)
  • 反之 → 死叉(卖出信号)

3. MACD

MACD 由三部分组成:DIF(快线-慢线)、DEA(DIF 的均线)、柱状图(DIF-DEA)。

def calc_macd(close, fast=12, slow=26, signal=9):
    ema_fast = close.ewm(span=fast, adjust=False).mean()
    ema_slow = close.ewm(span=slow, adjust=False).mean()
    dif = ema_fast - ema_slow
    dea = dif.ewm(span=signal, adjust=False).mean()
    macd_bar = (dif - dea) * 2
    return dif, dea, macd_bar

df["dif"], df["dea"], df["macd"] = calc_macd(df["close"])

print(df[["close", "dif", "dea", "macd"]].tail(10))

怎么用?

  • DIF 上穿 DEA → MACD 金叉
  • MACD 柱从负变正 → 趋势可能反转

4. RSI(相对强弱指标)

RSI 衡量"最近一段时间涨了多少、跌了多少",范围 0-100。

def calc_rsi(close, period=14):
    delta = close.diff()
    gain = delta.where(delta > 0, 0)
    loss = -delta.where(delta < 0, 0)
    avg_gain = gain.rolling(period).mean()
    avg_loss = loss.rolling(period).mean()
    rs = avg_gain / avg_loss
    return 100 - (100 / (1 + rs))

df["rsi_6"] = calc_rsi(df["close"], 6)
df["rsi_14"] = calc_rsi(df["close"], 14)

print(df[["close", "rsi_6", "rsi_14"]].tail(10))

怎么看?

  • RSI > 70 → 超买,可能回调
  • RSI < 30 → 超卖,可能反弹
  • 不同周期的 RSI 组合使用效果更好

5. 布林带(Bollinger Bands)

布林带由中轨(均线)和上下轨(均线 ± N 倍标准差)组成:

period = 20
df["boll_mid"] = df["close"].rolling(period).mean()
df["boll_std"] = df["close"].rolling(period).std()
df["boll_upper"] = df["boll_mid"] + 2 * df["boll_std"]
df["boll_lower"] = df["boll_mid"] - 2 * df["boll_std"]

print(df[["close", "boll_upper", "boll_mid", "boll_lower"]].tail(10))

怎么用?

  • 价格触及上轨 → 可能超买
  • 价格触及下轨 → 可能超卖
  • 布林带收窄 → 可能即将变盘

6. 成交量分析

df["vol_ma5"] = df["volume"].rolling(5).mean()
df["vol_ma20"] = df["volume"].rolling(20).mean()

# 量比:今日成交量 / 近 20 日平均
df["vol_ratio"] = df["volume"] / df["vol_ma20"]

# 筛选放量日(量比 > 2)
big_vol_days = df[df["vol_ratio"] > 2]
print(f"放量交易日: {len(big_vol_days)} 天")
print(big_vol_days[["close", "change_pct", "volume", "vol_ratio"]].tail())

五、生成交易信号

有了技术指标,就可以定义交易规则了。以最经典的双均线策略为例:

  • MA5 上穿 MA20 → 买入
  • MA5 下穿 MA20 → 卖出
# 判断金叉死叉
df["signal"] = 0

# 金叉:今天 ma5 > ma20,昨天 ma5 <= ma20
buy_mask = (df["ma5"] > df["ma20"]) & (df["ma5"].shift(1) <= df["ma20"].shift(1))
df.loc[buy_mask, "signal"] = 1

# 死叉
sell_mask = (df["ma5"] < df["ma20"]) & (df["ma5"].shift(1) >= df["ma20"].shift(1))
df.loc[sell_mask, "signal"] = -1

signals = df[df["signal"] != 0]
print(f"共产生 {len(signals)} 个信号")
print(signals[["close", "ma5", "ma20", "signal"]].tail(10))

你可以把这个逻辑换成任何其他指标组合,比如:

# MACD 金叉 + RSI 未超买
buy_macd = (df["dif"] > df["dea"]) & (df["dif"].shift(1) <= df["dea"].shift(1))
buy_rsi = df["rsi_14"] < 70
combined_buy = buy_macd & buy_rsi

六、批量处理多只股票

实际做量化,需要对多只股票批量计算指标:

from alphafeed import AlphaFeed

af = AlphaFeed(api_key="your-api-key")

symbols = ["600519.SH", "000858.SZ", "601318.SH", "000001.SZ", "600036.SH"]
dfs = af.klines.batch(symbols, period="1d", count=200, to_dataframe=True, show_progress=True)

results = []
for symbol, df in dfs.items():
    if len(df) < 60:
        continue

    df["ma5"] = df["close"].rolling(5).mean()
    df["ma20"] = df["close"].rolling(20).mean()
    df["rsi_14"] = calc_rsi(df["close"], 14)

    latest = df.iloc[-1]
    results.append({
        "symbol": symbol,
        "close": latest["close"],
        "ma5>ma20": latest["ma5"] > latest["ma20"],
        "rsi_14": round(latest["rsi_14"], 1),
    })

import pandas as pd
print(pd.DataFrame(results).to_string(index=False))

七、关于复权

如果你发现 K 线数据里有异常的大跳空(比如价格突然减半),大概率是因为除权除息。做技术分析前需要用复权数据:

  • 前复权:以最新价格为基准向前调整,适合回测
  • 后复权:以上市价格为基准向后调整,适合看长期收益
  • 不复权:原始价格

大多数数据源都支持指定复权方式。用前复权数据来算均线和回测,是最常见的做法。


八、总结

这篇文章覆盖了量化分析最基础的工作流:

  1. 获取 K 线数据(DataFrame 格式)
  2. rolling 计算均线
  3. ewm 计算 MACD
  4. 手写 RSI、布林带
  5. 生成交易信号(金叉死叉)
  6. 批量处理多只股票

这些全部是 pandas 的基本操作,不需要任何量化框架。掌握了这些,再去学 backtrader、vnpy 之类的框架,就会轻松很多——因为底层逻辑都是一样的。


量化没有想象中那么难,难的是坚持用数据验证每一个想法。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐