ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Python量化入门:获取BTC K线、计算MA/RSI并生成多空信号

Python量化入门:获取BTC K线、计算MA/RSI并生成多空信号 最近不少朋友在交流群里问到底怎么才能看到比特币行情背后那些“多空信号”“牛熊转换”的判断依据其实很多交易者口中说的“回落做多”“未见顶部信号”本质上都是一套基于 K 线与技术指标的计算逻辑。本文不预测行情也不讨论买卖点位而是从工程角度拆解一套完整的行情数据分析流程如何用 Python 获取 BTC/USDT 的真实 K 线数据计算均线、RSI 等常规指标并生成可量化的多空参考信号。这样下次再看到类似“牛继续”“未见明确顶部”的观点时你至少能自己写代码去验证背后的数据支撑。1. 为什么自己动手分析行情数据1.1 行情分析的核心不是“猜方向”很多刚接触加密货币的朋友会把行情分析理解为“预测明天涨还是跌”。但实际上正规的分析过程更接近“数据加工”先拿到足够长的历史 K 线数据再通过数学公式计算趋势、动量、波动率等特征最后基于这些特征形成“当前处于什么状态”的判断。以标题中提到的“回落继续布局多”为例这类判断通常依赖两类信息大周期趋势是否仍然向上例如日线 MA20、MA60 是否多头排列。短期是否出现超卖或回踩支撑的现象例如 RSI 是否进入低位区间。这些都不是拍脑袋想出来的而是可以通过 Python 中的 pandas 库精确计算的。当你自己写代码跑一遍就能知道某个“信号”到底是真实的指标触达还是只是事后看图说话。1.2 本文能用到的场景这套技术可以应用在多个场景中不只是比特币。只要数据源支持股票、ETF、主流币种的 K 线都可以用同样的代码处理。个人学习理解 K 线数据结构、指标公式、信号生成逻辑。数据可视化把收盘价、均线、RSI 绘制在一张图上直观看到指标变化。量化策略研究为后续做回测系统、自动提醒脚本打基础。交易复盘把自己之前的买卖点标注在图表上分析信号是否有效。本文的代码总量不大但覆盖了“数据获取 → 数据清洗 → 指标计算 → 信号输出 → 可视化”的完整链路适合作为量化分析入门的第一课。1.3 必要的读者提醒这里必须先说清楚比特币等加密货币价格波动极大24 小时连续交易且不同交易所之间价格可能存在差异。本文所有代码、信号、示例输出都仅用于技术学习和研究不构成任何投资建议。任何基于本文代码做出的投资决策风险都需要你自己承担。2. 环境准备与数据源选型2.1 运行环境本文示例以 Python 3.9 为演示环境操作系统使用 Windows 10/11 或 Ubuntu 20.04 均可核心代码不依赖特定系统。开发工具方面建议直接使用 PyCharm 或 VS Code方便查看 DataFrame 的数据内容和调试变量。需要说明的是以下依赖版本以你本地环境实际情况为准不必完全照搬版本号Python 3.9 pip 21.02.2 数据源有哪些选择获取比特币行情数据的途径很多比较常用的是交易所的公开 REST API。对于入门学习来说ccxt 是一个很合适的库它把多家交易所的接口统一封装成 Python 方法切换交易所时只需要改一行代码。常见的数据源选项数据源特点适用场景OKX API提供完整 K 线、深度、成交数据部分地区访问较稳定个人学习、策略研究Binance API交易量大K 线数据完整历史数据跨度长数据量大、需要长时间跨度的分析CoinGecko免费 API涵盖面广但 K 线粒度较粗快速查看全市场概况ccxt 库统一封装多家交易所 API希望代码可切换多交易所时考虑到不同网络环境下 API 可访问性差异较大本文统一使用 ccxt 编写示例默认以 OKX 作为数据源。如果你本地访问 Binance 更稳定则把代码中交易所初始化部分改成ccxt.binance()即可其他逻辑保持不变。2.3 安装 Python 依赖本文需要安装以下库ccxt统一交易所 API 接口库。pandas处理 K 线数据、计算指标。matplotlib绘制价格和指标图表。在终端执行pip install ccxt pandas matplotlib如果你的环境同时存在 Python 2需要使用pip3pip3 install ccxt pandas matplotlib安装完成后可以快速验证导入是否正常import ccxt import pandas as pd import matplotlib print(ccxt version:, ccxt.__version__) print(pandas version:, pd.__version__) print(matplotlib version:, matplotlib.__version__)如果正常打印版本号说明环境就绪。这里提醒一点ccxt版本更新较频繁不同版本在参数细节上可能有差异遇到问题时可以查看当前版本的官方文档。3. 核心概念OHLCV 与技术指标3.1 OHLCV 数据结构所有从交易所获取的行情数据本质上都是一组按时间排序的 K 线数据每条 K 线包含五个核心字段字段含义单位/说明timestampK 线开始时间毫秒时间戳UTC 时区open开盘价对应币种计价单位high最高价该 K 线周期内最高价low最低价该 K 线周期内最低价close收盘价该 K 线周期内最后一笔成交价volume成交量对应的交易量这种结构叫 OHLCV是技术分析最基础的数据形态。无论什么技术指标都会基于 OHLCV 做二次计算。需要注意加密货币交易所返回的时间戳默认是毫秒级。直接把毫秒时间戳转成北京时间需要先转换时区否则画图时会发现时间轴偏移了 8 个小时。3.2 均线 MA移动平均线Moving AverageMA是最常用的趋势指标用于抹平短期价格波动观察中长期方向。以 MA20 为例它表示最近 20 根 K 线收盘价的平均值。当收盘价在 MA20 上方且 MA20 方向向上时一般认为是多头趋势反之则是空头趋势。在 pandas 中计算均线非常方便df[ma20] df[close].rolling(window20).mean()这里需要注意的是rolling(window20).mean()计算的是前 20 个值的平均值。因此数据前 19 个位置会出现 NaN 空值后续做信号判断时需要处理。3.3 RSI 指标RSIRelative Strength Index相对强弱指数是衡量价格涨跌动量的指标取值范围在 0 到 100 之间。RSI 的计算逻辑可以简单理解为统计过去 N 个周期内的平均涨幅和平均跌幅。用两者比值换算成 0~100 之间的数值。常规判断标准是RSI 大于 70市场进入超买区价格可能回调。RSI 小于 30市场进入超卖区价格可能反弹。不过在强势趋势中RSI 可能长时间保持在 70 以上所以 RSI 只能作为辅助参考不能单独作为买卖依据。手工实现 RSI 的代码稍微多一点但逻辑并不复杂下面实战章节会给出完整实现。3.4 多空信号的基本逻辑“多空信号”不是交易所提供的现成数据而是我们根据指标自定义的规则。以本文示例来说可以定义两条核心规则MA5 上穿 MA20且 RSI 位于 50 以上视为“多头参考信号”。MA5 下穿 MA20且 RSI 位于 50 以下视为“空头参考信号”。所谓“上穿”也叫金叉指的是快线从下往上穿过慢线“下穿”则相反。这种信号在技术分析中非常常见用来识别趋势可能发生的转变。但请一定记住信号只是对历史数据的数学描述不代表未来价格一定上涨或下跌。它的价值在于给你一个可复现、可回测的判断框架而不是预测未来的水晶球。4. 完整实战获取 BTC/USDT 行情并计算指标接下来进入本文核心环节。我们分步骤实现一个完整的行情分析脚本从获取数据到输出信号全程使用真实公开数据。4.1 使用 ccxt 获取 K 线数据在项目目录下新建文件fetch_btc_kline.py输入以下代码# 文件路径fetch_btc_kline.py import ccxt import pandas as pd from datetime import datetime, timezone # 初始化交易所接口 # 如果你使用 Binance可改为 ccxt.binance() exchange ccxt.okx({ enableRateLimit: True, options: { defaultType: spot, # 现货交易 }, }) # 获取 BTC/USDT 1小时 K 线 symbol BTC/USDT timeframe 1h limit 300 # 获取最近 300 根 K 线 ohlcv exchange.fetch_ohlcv(symbol, timeframe, limitlimit) # 打印前 3 条数据观察原始格式 for row in ohlcv[:3]: print(row)运行脚本python fetch_btc_kline.py预期输出是类似下面的列表结构[1725292800000, 58000.0, 58500.0, 57800.0, 58300.0, 1523.4] [1725296400000, 58300.0, 58600.0, 58050.0, 58450.0, 1390.1] [1725300000000, 58450.0, 59100.0, 58320.0, 59000.0, 2105.8]每个列表元素依次对应时间戳、开盘价、最高价、最低价、收盘价、成交量。注意enableRateLimitTrue非常重要。它告诉 ccxt 在每次请求之间自动等待一段时间避免因为请求频率过高被交易所临时限制 IP。4.2 数据清洗与时间戳处理得到原始数据后需要把它转换成 pandas DataFrame同时把毫秒时间戳转换成可读的时间字符串。继续完善脚本# 将 OHLCV 列表转为 DataFrame df pd.DataFrame(ohlcv, columns[timestamp, open, high, low, close, volume]) # 将毫秒时间戳转换为 UTC 时间再转为北京时区 df[datetime] pd.to_datetime(df[timestamp], unitms, utcTrue).dt.tz_convert(Asia/Shanghai) # 删除原始时间戳列便于观察 df.drop(columns[timestamp], inplaceTrue) # 调整列顺序 df df[[datetime, open, high, low, close, volume]] print(df.head())这里的关键点是unitms。如果漏掉这个参数pandas 会把毫秒时间戳当成纳秒来解析最终得到的日期会变成 1970 年附近的异常值这是新手最常踩的坑之一。输出效果大致如下datetime open high low close volume 0 2024-09-03 08:00:0008:00 58000.0 58500.0 57800.0 58300.0 1523.4 1 2024-09-03 09:00:0008:00 58300.0 58600.0 58050.0 58450.0 1390.1 2 2024-09-03 10:00:0008:00 58450.0 59100.0 58320.0 59000.0 2105.8时间列已经变成可读的“北京时间”格式后续画图时会非常直观。4.3 计算 MA 与 RSI下面添加技术指标计算逻辑。把以下代码追加到脚本末尾# 计算移动平均线 df[ma5] df[close].rolling(window5).mean() df[ma20] df[close].rolling(window20).mean() # 计算 RSI14 期 def calc_rsi(series, period14): delta series.diff() # 涨幅delta 为正的部分否则为 0 gain delta.clip(lower0) # 跌幅delta 为负的部分取绝对值 loss -delta.clip(upper0) # 使用指数加权平均更贴近交易软件中的平滑逻辑 avg_gain gain.ewm(alpha1 / period, min_periodsperiod).mean() avg_loss loss.ewm(alpha1 / period, min_periodsperiod).mean() rs avg_gain / avg_loss rsi 100 - (100 / (1 rs)) return rsi df[rsi14] calc_rsi(df[close]) print(df.tail(10))关于 RSI 的实现这里使用的是 Wilder 平滑思想的近似写法通过ewm实现。它与部分交易软件中严格意义上的 Wilder 平滑存在细微差别但在绝大多数场景下不会影响信号的大方向判断。从输出中可以看到由于rolling(20)的存在前 19 行ma20和rsi14是 NaN。数据处理完成后后续计算信号时一般直接丢弃这些无效行。4.4 生成多空参考信号定义两条简单规则当ma5从下方穿过ma20且rsi14大于 50标记为“多头”。当ma5从上方穿过ma20且rsi14小于 50标记为“空头”。代码实现如下# 生成 ma5 与 ma20 的差值并判断金叉死叉 df[ma_diff] df[ma5] - df[ma20] df[ma_diff_prev] df[ma_diff].shift(1) # 金叉前一天差值 0当天差值 0 df[golden_cross] (df[ma_diff_prev] 0) (df[ma_diff] 0) # 死叉前一天差值 0当天差值 0 df[death_cross] (df[ma_diff_prev] 0) (df[ma_diff] 0) # 生成信号列 df[signal] df.loc[df[golden_cross] (df[rsi14] 50), signal] 多 df.loc[df[death_cross] (df[rsi14] 50), signal] 空 # 只查看出现信号的行 signal_df df[df[signal] ! ][[datetime, close, ma5, ma20, rsi14, signal]] print(signal_df.tail(10))这里有一个非常关键的细节shift(1)表示取前一行的值。它让代码具备了“比较当前行与上一行”的能力否则无法判断“上穿”还是“下穿”这个动作。4.5 可视化展示为了让结果更直观可以绘制一幅包含价格、均线和 RSI 的复合图。import matplotlib.pyplot as plt import matplotlib # 如果图表不显示中文可以取消下面这行注释改为你系统中的中文字体 # matplotlib.rcParams[font.sans-serif] [SimHei] # matplotlib.rcParams[axes.unicode_minus] False plt.rcParams[figure.figsize] (14, 8) plt.rcParams[figure.dpi] 100 fig, (ax1, ax2) plt.subplots( 2, 1, sharexTrue, gridspec_kw{height_ratios: [2.5, 1]} ) # 上图收盘价 均线 ax1.plot(df[datetime], df[close], labelClose, linewidth1) ax1.plot(df[datetime], df[ma5], labelMA5, linewidth1) ax1.plot(df[datetime], df[ma20], labelMA20, linewidth1) ax1.legend(locupper left) ax1.set_title(BTC/USDT 1H MA RSI Analysis) ax1.grid(alpha0.3) # 下图RSI ax2.plot(df[datetime], df[rsi14], labelRSI14, colorpurple, linewidth1) ax2.axhline(70, colorred, linestyle--, alpha0.5) ax2.axhline(30, colorgreen, linestyle--, alpha0.5) ax2.axhline(50, colorgray, linestyle--, alpha0.5) ax2.legend(locupper left) ax2.set_ylim(0, 100) ax2.grid(alpha0.3) plt.tight_layout() plt.savefig(btc_analysis.png) print(图表已保存为 btc_analysis.png)在这个图表里上半部分是收盘价与均线下半部分是 RSI 以及 30、50、70 三条参考线。运行后会在当前目录生成btc_analysis.png文件。4.6 运行结果说明把以上代码整合到一个完整脚本见 4.7 节后运行可能的输出结果如下最近信号 datetime close ma5 ma20 rsi14 signal 42 2024-09-05 04:00:0008:00 61200.0 61020.0 60500.0 55.3 多 87 2024-09-07 12:00:0008:00 59800.0 59900.0 60200.0 45.1 空这里需要强调的是以上数值只是示例输出实际运行时你会发现不同时间周期、不同交易所、不同参数下信号出现的位置完全不同。这正是量化的价值——它迫使你思考参数的含义而不是盲目相信某一个结论。同时要注意本文代码计算出的是“参考信号”不是“买卖指令”。实战中还需要结合成交量、市场深度、宏观消息面等信息综合判断。4.7 完整源码整合方便你直接运行下面是整合后的完整脚本# 文件路径btc_analysis.py import ccxt import pandas as pd import matplotlib.pyplot as plt # 1. 数据获取 exchange ccxt.okx({ enableRateLimit: True, options: {defaultType: spot}, }) symbol BTC/USDT timeframe 1h limit 300 ohlcv exchange.fetch_ohlcv(symbol, timeframe, limitlimit) df pd.DataFrame(ohlcv, columns[timestamp, open, high, low, close, volume]) df[datetime] pd.to_datetime(df[timestamp], unitms, utcTrue).dt.tz_convert(Asia/Shanghai) df.drop(columns[timestamp], inplaceTrue) df df[[datetime, open, high, low, close, volume]] # 2. 指标计算 df[ma5] df[close].rolling(window5).mean() df[ma20] df[close].rolling(window20).mean() def calc_rsi(series, period14): delta series.diff() gain delta.clip(lower0) loss -delta.clip(upper0) avg_gain gain.ewm(alpha1 / period, min_periodsperiod).mean() avg_loss loss.ewm(alpha1 / period, min_periodsperiod).mean() rs avg_gain / avg_loss return 100 - (100 / (1 rs)) df[rsi14] calc_rsi(df[close]) # 3. 信号判断 df[ma_diff] df[ma5] - df[ma20] df[ma_diff_prev] df[ma_diff].shift(1) df[golden_cross] (df[ma_diff_prev] 0) (df[ma_diff] 0) df[death_cross] (df[ma_diff_prev] 0) (df[ma_diff] 0) df[signal] df.loc[df[golden_cross] (df[rsi14] 50), signal] 多 df.loc[df[death_cross] (df[rsi14] 50), signal] 空 signal_df df[df[signal] ! ][[datetime, close, ma5, ma20, rsi14, signal]] print(最近信号) print(signal_df.tail(10)) # 4. 可视化 plt.figure(figsize(14, 8)) plt.plot(df[datetime], df[close], labelClose, linewidth1) plt.plot(df[datetime], df[ma5], labelMA5, linewidth1) plt.plot(df[datetime], df[ma20], labelMA20, linewidth1) plt.legend(locupper left) plt.title(BTC/USDT 1H MA RSI) plt.grid(alpha0.3) plt.tight_layout() plt.savefig(btc_analysis.png) print(图表已保存为 btc_analysis.png)这个脚本可以完整运行。你需要做的只是把ccxt.okx()换成自己本地可以正常访问的交易所然后等待几秒钟即可获得结果。5. 常见问题与排查思路实际运行过程中你可能会遇到一些问题。这里把最常见的几种情况整理成表格问题现象常见原因解决思路请求超时或连接失败本地网络无法访问对应交易所接口更换为可正常访问的数据源或检查网络连通性返回数据为空列表交易对不存在或起始时间设置错误确认交易对名称格式为BTC/USDT检查 since 参数时间列显示 1970 年时间戳单位解析错误确认使用pd.to_datetime(..., unitms)MA20 或 RSI 出现 NaN窗口期数据不足用dropna()删除前 N 行无效数据报错 “ExchangeNotAvailable”交易所接口临时故障或地区限制稍后重试或切换其他交易所信号一行都没有当前时间段没有满足条件的金叉死叉调整周期例如改为 4h 或 1d或扩大数据范围下面挑选两个高频问题详细说明排查过程。5.1 时间戳显示 1970 年这种现象几乎都是因为时间戳单位搞错了。大多数交易所返回的是毫秒时间戳长度是 13 位例如1725292800000。如果把它当成秒或者纳秒去解析就会出现异常年份。排查方法很简单print(df[timestamp].head())查看打印结果的最末三位是否是000。如果是说明确实是毫秒。然后确保转换代码为pd.to_datetime(df[timestamp], unitms, utcTrue)如果时间戳是秒级10 位则把unit参数改为s。5.2 信号结果为空信号为空有两种可能一是数据时间范围内确实没有触发“金叉/死叉 RSI 过滤”的完整条件二是你的数据量太少。解决思路把limit提高到500或1000。把时间周期从1h改成4h或1d。去掉rsi14的过滤条件先单独观察金叉死叉是否出现。如果去掉过滤后依然没有信号则说明这段时间是标准的单边行情均线没有发生交叉这也是正常现象。6. 工程实践与风险控制6.1 数据层建议在实际项目中不建议每次运行都重新拉取几百根 K 线。更合理的做法是把原始 K 线数据缓存到本地 CSV 或 SQLite避免重复请求浪费流量。定期增量更新数据例如每 5 分钟拉取一次最新 K 线只追加新数据。为每个请求设置超时重试机制避免单次网络抖动导致整个脚本崩溃。一个简单的缓存思路示例import os csv_path btc_1h.csv if os.path.exists(csv_path): df pd.read_csv(csv_path) print(已从本地缓存读取数据) else: ohlcv exchange.fetch_ohlcv(symbol, timeframe, limitlimit) df pd.DataFrame(ohlcv, columns[timestamp, open, high, low, close, volume]) df.to_csv(csv_path, indexFalse) print(已拉取数据并保存到本地)6.2 信号设计建议技术指标的组合方式是无限的但设计信号时要注意避免“过度拟合”。参数不要过于复杂MA5/MA20 和 RSI14 已经能覆盖很多场景。任何信号都必须能回溯验证。可以简单统计历史信号之后的收益率检验策略是否有正期望。RSI 在强趋势行情中会长时间钝化建议结合趋势过滤器例如只在 MA60 上方时才考虑多头信号。6.3 合规与风险意识这部分非常关键。本文所有代码和思路只能用于技术学习不能作为投资依据。加密货币交易本身波动剧烈很多交易所提供高倍杠杆一旦方向反了很可能损失全部本金。如果你打算把类似逻辑用于实盘请务必注意先使用模拟盘验证策略或者只用极小资金测试。做好仓位管理和风控止损不要因为“信号出现”就满仓操作。保留每一次信号的完整快照数据便于复盘和优化。不做未经验证的自动化实盘程序避免程序 Bug 或交易所接口异常造成不可控损失。7. 总结与下一步学习方向通过本文的实战你应该已经掌握了从交易所拉取 BTC/USDT 行情数据、清洗时间戳、计算 MA 与 RSI、生成多空参考信号并可视化的完整流程。这套流程不仅适用于比特币也适用于其他交易对和大多数主流交易所的公开 K 线数据。下一步可以从以下方向继续深入扩展更多指标例如 MACD、BOLL、KDJ。搭建一个简单的历史回测框架把“信号生成”和“收益统计”衔接起来。把数据源换成更多维度的数据例如资金费率、持仓量、链上活跃地址数。使用定时任务实现每日自动生成行情分析报告。如果你之前只会在交易软件里看 K 线那么现在你已经拥有了自己写代码分析行情的初步能力。建议你先跑通文中的完整脚本把btc_analysis.png生成出来然后尝试调整limit、timeframe和指标参数观察不同设置下信号的变化规律。只有亲手改过、跑过、对比过才能真正理解这些指标在不同行情下的表现差异。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进