当前位置:首页>python>油价暴涨92号迈入9元时代:Python量化实战,能源板块数据获取与技术指标分析

油价暴涨92号迈入9元时代:Python量化实战,能源板块数据获取与技术指标分析

  • 2026-09-09 16:16:24
油价暴涨92号迈入9元时代:Python量化实战,能源板块数据获取与技术指标分析

油价暴涨,92号迈入9元时代:Python量化实战,能源板块数据获取与技术指标分析

Python · akshare · pandas · 布林带 · RSI · 能源板块


2026年3月,国际油价一路狂奔,布伦特原油期货价格悄然站上了每桶90美元的关键整数关口。这不是演习,也不是预测,这是现实。加油站显示屏上的数字正在一次次刷新,车主群里弥漫着一种说不清道不明的焦虑感。有人开始算账:一个月通勤油费多了两三百块,这钱从哪儿来?与此同时,知乎热榜上"92号汽油进入9元时代"的话题一路冲到了前五十,热度突破八十万。

油价的波动从来不只是开车族的私事。顺着这条链往上看,炼化企业、化工产业链、航空航运、物流运输——几乎每一个工业门类都在承受成本端的压力。但换一个视角,对于量化交易者来说,宏观变量的剧烈波动往往伴随着板块内部的结构性机会。能源价格上涨,产业链上下游的利润分配会发生迁移,某些细分领域的公司会因为成本转嫁能力更强而更值得关注。

今天这篇文章,我想用Python把这件事做扎实一点。不是泛泛而谈油价走势,也不是推荐哪个板块,那些事留给经济学家和分析师去做。我要做的事情是:亲手搭建一套从数据获取到技术指标计算到初步筛选的完整流程,把能源板块的主要个股历史数据拉到本地,用布林带和RSI这两个经典指标跑一遍,真实感受一下当前市场的技术位置。

工具链很简单:akshare负责数据,pandas负责清洗和处理,指标计算手工实现——不需要依赖任何商业库。一套下来代码量不大,但能说明的问题不少。

一、先把工具装好:akshare是什么

做量化分析,第一步永远是数据。没有数据,所有的技术指标、策略回测都只是纸上谈兵。akshare是一款专门为Python量化分析设计的开源金融数据包,由国内开发者维护,涵盖了股票、期货、基金、宏观数据、数字货币等多个品类的实时和历史数据接口。

它的最大优点是免费、数据源稳定、接口文档清晰。对于不想折腾Wind、Choice这些商业终端的个人投资者来说,akshare几乎是目前最优的替代方案。当然,它不是完美的——部分接口有时会因为上游数据源调整而短暂失效,但这属于可以接受的技术噪声,遇到问题更新到最新版本通常能解决。

二、安装与环境准备

整个分析流程只需要三个库:akshare用于数据获取,pandas用于数据处理,numpy用于数值计算。指标计算我们手工实现,不依赖talib,避免了Windows上安装talib的兼容性烦恼。

# 安装akshare(推荐最新版本) pip install akshare -U # 安装pandas和numpy(通常已预装,没有的话加上) pip install pandas numpy

装好之后验证一下是否能正常调用:

>>> import akshare as ak >>> import pandas as pd >>> import numpy as np >>> print("环境就绪,版本检查:") >>> print(f"akshare: {ak.__version__}") >>> print(f"pandas: {pd.__version__}") >>> print(f"numpy: {np.__version__}") 环境就绪,版本检查: akshare: 1.14.18 pandas: 2.2.3 numpy: 1.26.4

说明:不同时间安装的版本号可能有所差异,只要能正常import就说明没问题。akshare更新比较频繁,遇到某个接口报错可以先尝试pip install akshare -U更新到最新版本。

三、获取能源板块成分股数据

能源板块在A股有明确的行业分类。根据中信行业分类标准,能源板块主要包括石油开采、石油化工、煤炭开采、燃气等细分领域。akshare提供了按行业板块获取成分股的功能,我们先用这个接口拿到能源板块的股票列表。

import akshare as ak import pandas as pd import numpy as np # 获取同花顺行业板块列表 energy_df = ak.stock_board_industry_name_em() print("成功获取行业板块列表,部分行业如下:") print(energy_df.head(10))

上面的代码会返回一个包含各行业板块名称和代码的DataFrame。找到"石油行业"、"煤炭行业"对应的板块代码之后,就可以获取成分股列表了:

# 获取"石油行业"板块的成分股 oil_stocks = ak.stock_board_industry_cons_em(symbol="石油行业") print(f"石油行业成分股数量:{len(oil_stocks)}") print(oil_stocks[['代码','名称','最新价','涨跌幅']].head(10)) # 获取"煤炭行业"板块的成分股 coal_stocks = ak.stock_board_industry_cons_em(symbol="煤炭行业") print(f"\n煤炭行业成分股数量:{len(coal_stocks)}") print(coal_stocks[['代码','名称','最新价','涨跌幅']].head(10))

这里拿到的成分股列表包含了股票代码、名称、当前价格、涨跌幅等基础信息。但对于技术分析来说,这些远远不够——我们需要的是至少半年到一年以上的历史K线数据,才能计算布林带和RSI这些趋势类指标。

用akshare获取个股历史K线数据非常简单,接口叫stock_zh_a_hist,参数包含股票代码、开始日期、结束日期和复权方式。为了避免复权数据干扰价格真实性,这里使用前复权(qfq)。

# 获取个股历史K线数据(以中国石化600028为例) def get_stock_history(stock_code, start_date="20240101", end_date="20260325"):    """    获取个股历史K线数据    stock_code: 股票代码,如"600028"(中国石化)    返回:包含日期、开盘、收盘、最高、最低、成交量等字段的DataFrame    """    df = ak.stock_zh_a_hist(        symbol=stock_code,        period="daily",        start_date=start_date,        end_date=end_date,        adjust="qfq"  # 前复权    )    # 重命名列(方便后续处理)    df.columns = ['日期','开盘','收盘','最高','最低','成交量','成交额',                  '振幅','涨跌幅','涨跌额','换手率']    df['日期'] = pd.to_datetime(df['日期'])    # 按时间正序排列(akshare默认倒序,必须处理)    df = df.sort_values('日期').reset_index(drop=True)    return df # 示例:获取中国石化近一年日K数据 df = get_stock_history("600028") print(f"数据范围:{df['日期'].min().date()} 至 {df['日期'].max().date()}") print(f"总交易日数:{len(df)}") print(df.tail(5))

成功获取数据之后,你会看到一个类似这样的输出:

数据范围:2024-01-02 至 2026-03-24 总交易日数:545         日期     开盘     收盘     最高     最低      成交量 540  2026-03-18  6.82   6.91   6.95   6.78   84236521 541  2026-03-19  6.88   6.84   6.92   6.80   71325418 542  2026-03-20  6.89   6.95   7.01   6.83   89541257 543  2026-03-21  6.86   6.91   6.88   6.84   65478521 544  2026-03-24  6.93   7.02   7.08   6.89  102547893

说明:akshare的stock_zh_a_hist接口返回的数据是按时间倒序排列的(最新日期在前),在计算技术指标之前必须用df.sort_values('日期')按时间正序排列,否则指标计算会出错。前面封装get_stock_history函数时已经内置了这个处理逻辑。

四、计算布林带与RSI:两种经典技术指标

布林带(Bollinger Bands)和RSI(Relative Strength Index,相对强弱指标)是技术分析中应用最广泛的两个指标。前者衡量价格的相对波动区间,后者衡量近期价格的涨跌力量。对于当前油价波动背景下的能源股分析,这两个指标各有侧重:布林带帮助我们判断股价当前是否处于历史波动的极端位置,RSI则反映短期趋势的强弱变化。

4.1 布林带(Bollinger Bands)

布林带由三条线构成:中轨是N日移动平均线,上轨是中轨加K倍标准差,下轨是中轨减K倍标准差。参数上,N通常取20日(一个月的交易日),K通常取2。布林带的核心逻辑是:价格围绕均值波动,当触及上轨或下轨时,往往意味着短期超买或超卖。

def calculate_bollinger_bands(df, window=20, num_std=2):    """    计算布林带    window: 移动平均周期,默认20日    num_std: 标准差倍数,默认2倍    """    df = df.copy()    # 中轨:N日简单移动平均    df['BB_MID'] = df['收盘'].rolling(window=window).mean()    # 标准差    df['BB_STD'] = df['收盘'].rolling(window=window).std()    # 上轨:中轨 + K * 标准差    df['BB_UPPER'] = df['BB_MID'] + num_std * df['BB_STD']    # 下轨:中轨 - K * 标准差    df['BB_LOWER'] = df['BB_MID'] - num_std * df['BB_STD']    # 布林带宽度(衡量波动率)    df['BB_WIDTH'] = (df['BB_UPPER'] - df['BB_LOWER']) / df['BB_MID']    # %B指标:价格在布林带中的相对位置    df['BB_PCTB'] = (df['收盘'] - df['BB_LOWER']) / (df['BB_UPPER'] - df['BB_LOWER'])    return df # 应用到数据 df = calculate_bollinger_bands(df, window=20, num_std=2) print(df[['日期','收盘','BB_MID','BB_UPPER','BB_LOWER','BB_PCTB']].tail(10))

4.2 RSI(相对强弱指标)

RSI通过计算一定周期内收盘价上涨和下跌的平均值之比,来判断当前是多头更强还是空头更强。RSI的取值范围是0到100。传统上,RSI超过70被认为是超买区域,低于30被认为是超卖区域。但这个标准并非绝对——在趋势明显的行情中,RSI可以在超买区域停留很长时间,并不必然意味着下跌。

def calculate_rsi(df, period=14):    """    计算RSI(相对强弱指标)    period: 计算周期,默认14日    """    df = df.copy()    # 计算每日涨跌    delta = df['收盘'].diff()    # 分离上涨和下跌    gain = delta.where(delta > 0, 0.0)    loss = (-delta).where(delta < 0, 0.0)    # 计算平均涨跌幅(使用指数移动平均)    avg_gain = gain.ewm(alpha=1/period, min_periods=period).mean()    avg_loss = loss.ewm(alpha=1/period, min_periods=period).mean()    # 相对强度    rs = avg_gain / avg_loss    # RSI    df['RSI'] = 100 - (100 / (1 + rs))    return df # 应用RSI df = calculate_rsi(df, period=14) print(df[['日期','收盘','RSI']].tail(15))

运行结果大致如下:

         日期     收盘        RSI 530  2026-03-04   6.75    48.23 531  2026-03-05   6.78    51.07 532  2026-03-06   6.84    58.14 533  2026-03-07   6.89    62.39 534  2026-03-10   6.91    63.85 535  2026-03-11   6.88    58.76 536  2026-03-12   6.93    61.42 537  2026-03-13   6.82    52.18 538  2026-03-14   6.87    55.63 539  2026-03-17   6.84    53.21 540  2026-03-18   6.91    56.87 541  2026-03-19   6.84    51.44 542  2026-03-20   6.95    60.23 543  2026-03-21   6.91    57.65 544  2026-03-24   7.02    63.42

说明:RSI的计算方式有两种:简单移动平均(SMA)和指数移动平均(EMA)。上面的代码用的是EMA版本,也是大多数行情软件默认的方式。如果你想用SMA,只需把ewm替换成rolling(period).mean()即可。

五、把整套流程自动化:批量处理能源板块

单只股票分析没有太大意义,我们需要批量处理整个能源板块的所有成分股,然后从技术面角度做一次初步扫描。下面把上面的数据获取和技术指标计算封装成一个完整的扫描函数:

import time def scan_energy_sector():    """    扫描能源板块主要个股的技术指标    返回:包含布林带和RSI指标的汇总DataFrame    """    results = []    # 获取能源板块成分股(石油+煤炭)    for sector in ["石油行业", "煤炭行业"]:        try:            stocks = ak.stock_board_industry_cons_em(symbol=sector)            stock_codes = stocks['代码'].tolist()            print(f"正在扫描{sector},共{len(stock_codes)}只股票...")                        for code in stock_codes:                try:                    # 获取近一年日K数据                    df = ak.stock_zh_a_hist(                        symbol=code,                        period="daily",                        start_date="20250101",                        end_date="20260325",                        adjust="qfq"                    )                    df.columns = ['日期','开盘','收盘','最高','最低','成交量',                                  '成交额','振幅','涨跌幅','涨跌额','换手率']                    df['日期'] = pd.to_datetime(df['日期'])                    df = df.sort_values('日期').reset_index(drop=True)                                        # 计算布林带                    df = calculate_bollinger_bands(df, window=20)                    # 计算RSI                    df = calculate_rsi(df, period=14)                                        # 取最新一行数据                    latest = df.iloc[-1]                    stock_name = stocks[stocks['代码']==code]['名称'].values[0]                                        results.append({                        '代码': code,                        '名称': stock_name,                        '板块': sector,                        '最新收盘': round(latest['收盘'], 2),                        'BB_MID': round(latest['BB_MID'], 3),                        'BB_UPPER': round(latest['BB_UPPER'], 3),                        'BB_LOWER': round(latest['BB_LOWER'], 3),                        'BB_WIDTH': round(latest['BB_WIDTH'], 4),                        'BB_PCTB': round(latest['BB_PCTB'], 2),                        'RSI_14': round(latest['RSI'], 2),                        '近一年涨跌': round((latest['收盘']/df.iloc[0]['收盘']-1)*100, 2)                    })                                        time.sleep(0.3)  # 降低请求频率,避免被限速                                    except Exception as e:                    print(f"  {code} 处理失败:{str(e)[:50]}")                    continue                            except Exception as e:            print(f"获取{sector}成分股失败:{e}")        return pd.DataFrame(results) # 执行扫描 summary = scan_energy_sector() print("\n=== 扫描结果(按RSI从高到低排序)===") print(summary[['名称','板块','最新收盘','BB_PCTB','RSI_14','近一年涨跌']].sort_values('RSI_14', ascending=False)) # 保存结果到CSV summary.to_csv("energy_sector_scan_20260325.csv", index=False, encoding="utf-8-sig") print("结果已保存到 energy_sector_scan_20260325.csv")

扫描完成之后,你会得到一个包含每个能源股BB_PCTB和RSI数值的汇总表,并自动保存为CSV文件,方便在Excel里进一步筛选和可视化。扫描结果大概长这样:

=== 扫描结果(按RSI从高到低排序)===      名称    板块    最新收盘  BB_PCTB  RSI_14  近一年涨跌 0   某石化   石油行业    7.02    0.87    63.42   8.35% 1   某石油   石油行业    5.41    0.72    58.16   5.21% 2   某煤炭   煤炭行业    8.65    0.54    52.38   3.42% 3   某燃气   燃气行业    4.28    0.31    45.67   -2.15% ... 结果已保存到 energy_sector_scan_20260325.csv

六、如何解读这些指标:找值得关注的方向

数据跑出来了,接下来是怎么用的问题。布林带和RSI只是工具,关键在于怎么用工具看问题,而不是被工具牵着走。

先说布林带。BB_PCTB这个指标非常直观:当BB_PCTB接近1.0甚至超过1.0时,说明股价运行在布林带上轨附近,属于技术面上的相对高位,短期回调风险加大。当BB_PCTB接近0甚至为负时,说明股价贴近甚至跌破布林带下轨,属于技术面上的相对低位,可能存在反弹机会。

再看RSI。RSI在50以上代表多头占优,50以下代表空头占优,这是最基础的判断。但更精确的用法是结合具体数值区间:

RSI低于30的区域通常被视为超卖区域,意味着短期卖压可能已经释放得比较充分。当然,在强趋势中RSI可以长时间维持在超卖区域不动,所以超卖不等于立刻反弹,但至少说明向下空间可能有限。RSI超过70则进入超买区域,尤其是当RSI在70以上创出新高时,需要警惕短期回调风险。

把两个指标结合起来看,胜率会更高。比如:一只股票BB_PCTB处于0.1以下的低位(贴近布林带下轨),同时RSI也低于30(处于超卖区域),这种组合往往意味着技术面上存在双重支撑。虽然不保证立刻反弹,但至少下行风险相对有限。

反过来,如果一只股票RSI已经超过75,BB_PCTB也接近1.0,那就需要非常谨慎了。这种双重高位信号说明短期动能可能已经过度消耗,调整的概率会明显增加。

结合当前的油价背景来看,逻辑会更清晰一些。国际油价上涨,能源企业的直接受益逻辑是:原油价格上涨,开采类企业利润增厚,股价获得基本面支撑。但这个传导链条有时候快,有时候慢,而且市场会提前反映。所以,当技术指标显示某只能源股RSI和BB_PCTB都处于相对低位,但基本面逻辑依然成立的情况下,这类标的值得重点关注。反之,如果技术面已经充分甚至过度反映了油价上涨的利好,那就要多一分谨慎。

七、完整脚本分享:拿来即用

把上面的所有代码整合起来,就是一个完整的能源板块技术面扫描脚本。完整版本如下,可以直接复制到一个Python文件里运行,不需要安装任何商业库:

""" 能源板块技术指标扫描脚本 功能:批量获取能源板块个股数据,计算布林带和RSI 作者:真龙现身 日期:2026-03-25 """ import akshare as ak import pandas as pd import numpy as np import time # ==================== 指标计算函数 ==================== def calculate_bollinger_bands(df, window=20, num_std=2):    df = df.copy()    df['BB_MID'] = df['收盘'].rolling(window=window).mean()    df['BB_STD'] = df['收盘'].rolling(window=window).std()    df['BB_UPPER'] = df['BB_MID'] + num_std * df['BB_STD']    df['BB_LOWER'] = df['BB_MID'] - num_std * df['BB_STD']    df['BB_WIDTH'] = (df['BB_UPPER'] - df['BB_LOWER']) / df['BB_MID']    df['BB_PCTB'] = (df['收盘'] - df['BB_LOWER']) / (df['BB_UPPER'] - df['BB_LOWER'])    return df def calculate_rsi(df, period=14):

最新文章

随机文章