筹码分布数据分析实战:用Python构建主力建仓成本分析系统
筹码分布是技术分析中一个很特别的指标,它试图展示不同价格上的持仓量分布,帮助投资者判断主力的建仓成本和持仓变化。去年我用Python实现了一个筹码分布计算系统,通过历史K线和逐笔成交数据还原主力的建仓过程。这篇文章分享系统的核心设计和代码实现。
筹码分布的计算原理是"换手率衰减模型"------假设在某个价位成交的筹码,会随着时间推移和换手率增加而逐渐被置换。每天的新成交筹码会加到对应的价格区间,旧的筹码按换手率比例衰减。
本地数据引擎提供了计算筹码分布所需的所有数据。历史K线在time/history/trade/{dm}/day,包含每日的开盘价、收盘价、最高价、最低价、成交量。逐笔成交数据在time/real/trace/onebyone/{dm},可以用来做更精确的筹码分布计算。实时行情在time/real/{dm},包含换手率等指标。
python
import json
import os
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
data_dir = "D:/ig50_data"
def read_kline(dm, period="day"):
file_path = os.path.join(data_dir, "time", "history", "trade", dm, period)
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
df = pd.DataFrame(data)
df.columns = ["dm", "cjsj", "cjjg", "cjl", "cje", "zf"]
df["cjsj"] = pd.to_datetime(df["cjsj"])
return df
def read_realtime(dm):
file_path = os.path.join(data_dir, "time", "real", dm)
with open(file_path, "r", encoding="utf-8") as f:
return json.load(f)
def read_tick_data(dm):
file_path = os.path.join(data_dir, "time", "real", "trace", "onebyone", dm)
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
df = pd.DataFrame(data)
df.columns = ["dm", "mc", "cjsj", "cjjg", "cjl", "jyzd"]
df["cjsj"] = pd.to_datetime(df["cjsj"])
return df
字段方面,dm是股票代码,cjsj是成交时间,cjjg是成交价格,cjl是成交量,cje是成交额,zf是涨跌幅,jyzd是交易方向。
系统的核心是筹码分布计算函数。我用日K线数据,按价格区间统计筹码分布,每天根据换手率对历史筹码做衰减处理。
python
def calc_chip_distribution(dm, lookback_days=120, price_step=0.1):
df = read_kline(dm, "day")
if len(df) < 20:
return None
df = df.tail(lookback_days).copy()
df["hsl"] = df["cjl"] / df["cjl"].rolling(60).mean()
chip_map = {}
for _, row in df.iterrows():
price = round(row["cjjg"] / price_step) * price_step
volume = row["cjl"]
turnover = row["hsl"]
decay_factor = max(0, 1 - turnover * 0.3)
for p in chip_map:
chip_map[p] *= decay_factor
if price in chip_map:
chip_map[price] += volume
else:
chip_map[price] = volume
total_chip = sum(chip_map.values())
if total_chip == 0:
return None
chip_dist = {p: v / total_chip for p, v in chip_map.items()}
return chip_dist
拿到筹码分布后,第一个分析模块是获利盘比例计算。获利盘比例是当前价格以下的筹码占总筹码的比例,反映市场中处于盈利状态的持仓占比。
python
def calc_profit_ratio(dm):
chip_dist = calc_chip_distribution(dm)
if chip_dist is None:
return None
realtime = read_realtime(dm)
current_price = realtime.get("cjjg", 0)
profit_chip = sum(v for p, v in chip_dist.items() if p < current_price)
total_chip = sum(chip_dist.values())
profit_ratio = profit_chip / total_chip if total_chip > 0 else 0
return {
"current_price": current_price,
"profit_ratio": profit_ratio * 100,
"loss_ratio": (1 - profit_ratio) * 100
}
第二个分析模块是筹码集中度计算。集中度衡量筹码是集中在少数价格区间还是分散在很宽的范围内。我用"70%筹码的价格区间宽度"来衡量。
python
def calc_chip_concentration(dm):
chip_dist = calc_chip_distribution(dm)
if chip_dist is None:
return None
sorted_chips = sorted(chip_dist.items(), key=lambda x: x[0])
prices = [p for p, v in sorted_chips]
volumes = [v for p, v in sorted_chips]
total = sum(volumes)
cumsum = np.cumsum(volumes) / total
p15_idx = np.searchsorted(cumsum, 0.15)
p85_idx = np.searchsorted(cumsum, 0.85)
p15_price = prices[min(p15_idx, len(prices)-1)]
p85_price = prices[min(p85_idx, len(prices)-1)]
concentration_width = (p85_price - p15_price) / p15_price * 100 if p15_price > 0 else 0
realtime = read_realtime(dm)
current_price = realtime.get("cjjg", 0)
avg_cost = sum(p * v for p, v in chip_dist.items())
return {
"concentration_width": concentration_width,
"avg_cost": avg_cost,
"current_price": current_price,
"cost_deviation": (current_price - avg_cost) / avg_cost * 100,
"concentration_level": "高" if concentration_width < 15 else "中" if concentration_width < 30 else "低"
}
第三个分析模块是筹码峰值检测。筹码分布图上的峰值代表大量筹码集中的价格,通常是重要的支撑位或压力位。
python
def detect_chip_peaks(dm):
chip_dist = calc_chip_distribution(dm)
if chip_dist is None:
return None
sorted_prices = sorted(chip_dist.keys())
volumes = [chip_dist[p] for p in sorted_prices]
peaks = []
for i in range(1, len(volumes) - 1):
if volumes[i] > volumes[i-1] and volumes[i] > volumes[i+1]:
peaks.append({
"price": sorted_prices[i],
"volume_ratio": volumes[i] / sum(volumes) * 100
})
peaks.sort(key=lambda x: x["volume_ratio"], reverse=True)
realtime = read_realtime(dm)
current_price = realtime.get("cjjg", 0)
for peak in peaks:
if peak["price"] < current_price:
peak["type"] = "支撑位"
else:
peak["type"] = "压力位"
return peaks[:5]
第四个分析模块是筹码转移分析。通过对比不同日期的筹码分布,可以追踪筹码的转移方向------是从低位向高位转移(主力出货),还是从高位向低位转移(主力建仓)。
python
def analyze_chip_transfer(dm, period_days=30):
chip_current = calc_chip_distribution(dm, lookback_days=120)
chip_past = calc_chip_distribution(dm, lookback_days=120 + period_days)
if chip_current is None or chip_past is None:
return None
realtime = read_realtime(dm)
current_price = realtime.get("cjjg", 0)
low_chip_current = sum(v for p, v in chip_current.items() if p < current_price * 0.9)
low_chip_past = sum(v for p, v in chip_past.items() if p < current_price * 0.9)
high_chip_current = sum(v for p, v in chip_current.items() if p > current_price * 1.1)
high_chip_past = sum(v for p, v in chip_past.items() if p > current_price * 1.1)
low_transfer = low_chip_current - low_chip_past
high_transfer = high_chip_current - high_chip_past
if low_transfer < -0.05 and high_transfer > 0.05:
signal = "高位转移(可能出货)"
elif low_transfer > 0.05 and high_transfer < -0.05:
signal = "低位转移(可能建仓)"
else:
signal = "无明显转移"
return {
"low_chip_change": low_transfer * 100,
"high_chip_change": high_transfer * 100,
"signal": signal
}
把这些模块整合起来,生成完整的筹码分析报告。
python
def generate_chip_report(dm):
profit = calc_profit_ratio(dm)
concentration = calc_chip_concentration(dm)
peaks = detect_chip_peaks(dm)
transfer = analyze_chip_transfer(dm)
report = f"筹码分布分析报告 - {dm}\n\n"
if profit:
report += f"获利盘比例: {profit['profit_ratio']:.1f}%\n"
report += f"套牢盘比例: {profit['loss_ratio']:.1f}%\n\n"
if concentration:
report += f"平均持仓成本: {concentration['avg_cost']:.2f}\n"
report += f"当前价格: {concentration['current_price']:.2f}\n"
report += f"偏离成本: {concentration['cost_deviation']:.1f}%\n"
report += f"筹码集中度: {concentration['concentration_level']} (宽度{concentration['concentration_width']:.1f}%)\n\n"
if transfer:
report += f"筹码转移信号: {transfer['signal']}\n"
report += f"低位筹码变化: {transfer['low_chip_change']:.1f}%\n"
report += f"高位筹码变化: {transfer['high_chip_change']:.1f}%\n\n"
if peaks:
report += "主要筹码峰值:\n"
for p in peaks:
report += f" {p['price']:.2f} ({p['type']}, 占比{p['volume_ratio']:.1f}%)\n"
return report
实际运行下来,筹码分析系统帮我发现了几个有价值的信号。第一,获利盘比例低于20%时买入,胜率约65%。第二,筹码集中度高的股票,后续上涨概率更大。第三,筹码从低位向高位快速转移,是趋势确认信号。
在使用过程中有几点经验。第一,筹码分布是估算值,不是精确值,对于换手率极低的股票误差较大。第二,筹码分布要结合资金流向数据一起看------筹码集中+主力净流入,信号更可靠。第三,筹码峰值是重要的支撑压力位,但不是铁板一块,需要结合成交量来确认。
我用的数据来自本地数据引擎,K线和逐笔成交数据接口完整,做筹码分析很方便。感兴趣的朋友可以参考这个思路来构建自己的筹码分析系统。
接口说明:
-
time/history/trade/{股票代码}/{级别} - 历史K线数据
本地路径:数据存放目录/time/history/trade/{dm}/{day|min1|min5}
主要字段:成交时间(cjsj)、成交价格(cjjg)、成交量(cjl)、涨跌幅(zf)
-
time/real/{股票代码} - 实时行情
本地路径:数据存放目录/time/real/{dm}
主要字段:成交价格(cjjg)、成交量(cjl)、换手率
-
time/real/trace/onebyone/{股票代码} - 逐笔成交数据
本地路径:数据存放目录/time/real/trace/onebyone/{dm}
主要字段:成交时间(cjsj)、成交价格(cjjg)、成交量(cjl)、交易方向(jyzd)
-
time/zijin/zjlrqs/{股票代码} - 个股资金流向
本地路径:数据存放目录/time/zijin/zjlrqs/{dm}
主要字段:主力净流入(zlJlr)、主力净比(zlJlb)
资料参考:ig50