2025-09-30

日経平均株価の構成銘柄を取得

日経平均株価は、日本経済新聞社が算出・公表している日本の株式市場の代表的な株価指数の一つです。東京証券取引所プライム市場に上場している約 2,000 銘柄の株式のうち、取引が活発で流動性が高い 225 銘柄を、日本経済新聞社が選定し算出します。構成銘柄は 4 月と 10 月の年 2 回、上限各 3 銘柄の定期入れ替えが実施されています。

Wikipedia より引用、編集

日経平均株価の構成銘柄を Python で取得しようと Jupyter Lab 上で、参考サイト [1] に従って試したところ、以下のようにエラーが出て取得できませんでした。

dfs = pd.read_html("https://indexes.nikkei.co.jp/nkave/index/component")
---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)

...
(途中省略)
...

    612 def http_error_default(self, req, fp, code, msg, hdrs):
--> 613     raise HTTPError(req.full_url, code, msg, hdrs, fp)

HTTPError: HTTP Error 403: Forbidden

ところが、端末エミュレータ上で同じ URL を wget コマンドで同じサイトを取得してみたところ、難なく出来てしまいました。

上記 Pandas で読み込むやり方でも、何か工夫をすれば読み込めるのかもしれませんが、とりあえず Python から wget コマンドを実行して、読み込みたいサイトを一旦ローカルファイルに保存してから、そのファイルを Pandas で読み込むことで、やりたかったことをできるようになったので、備忘録にしました。

下記の OS 環境で動作確認をしています。

Fedora Linux Workstation 42 x86_64
Python 3.13.7
beautifulsoup4 4.14.2
html5lib 1.1
JupyterLab 4.4.9
lxml 6.0.2
pandas 2.3.3
wget2 2.2.0-5 (rpm)

サンプル

以下、Jupyter Lab 上で実行することを想定しています。

最初に利用するライブラリをまとめてインポートします。

import os
import subprocess

import pandas as pd

外部コマンドを実行する関数です。ピッタリな用途の関数が公開されていたので、そのまま流用させていただきました。

# Reference:
# https://brightdata.jp/blog/各種ご利用方法/wget-with-python
def execute_command(command):
    """
    Execute a CLI command and return the output and error messages.

    Parameters:
        - command (str): The CLI command to execute.

    Returns:
        - output (str): The output generated by the command.
        - error (str): The error message generated by the command, if any.
    """
    try:
        # execute the command and capture the output and error messages
        process = subprocess.Popen(
            command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
        )
        output, error = process.communicate()
        output = output.decode("utf-8")
        error = error.decode("utf-8")
        # return the output and error messages
        return output, error
    except Exception as e:
        # if an exception occurs, return the exception message as an error
        return None, str(e)

日経平均株価の構成銘柄をデータフレームで取得する処理です。

# wget コマンドで対象サイトをダウンロード
file = "component"
# ファイルが存在していれば削除 
if os.path.exists(file):
    os.remove(file)
output, error = execute_command(f"wget https://indexes.nikkei.co.jp/nkave/index/{file}")

# 保存された HTML ファイルを Pandas で読み込む
dfs = pd.read_html(file)
# 複数のデータフレームを結合して、コード列でソート
df = pd.concat(dfs).sort_values("コード", ignore_index=True)
df

 

参考サイト

  1. Python, pandasでwebページの表(htmlのtable)をスクレイピング | note.nkmk.me
  2. 構成銘柄一覧 - 日経平均プロフィル

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



このエントリーをはてなブックマークに追加

2025-09-16

Ta-Lib を Python で利用する

TA-Lib, Technical Analysis Library は 2001 年にリリースされ、20 年以上経った今でも広く利用されている著名なアルゴリズムを提供しています。コードは安定しており、長年にわたる検証を経ています。200 以上のテクニカル指標をサポートしており、API は C/C++ で記述されており Python ラッパー (wrapper) も提供されています。TA-Lib は BSD License (BSD-2-Clause license) の元で配布されているオープンソースのライブラリです。

以前は Linux 上で TA-Lib の Python 用パッケージを pip でインストールしてもビルドが必要で、しかもエラーでビルドできませんでした。自力でエラーを解決できなかったので TA-Lib の利用を避けていました。しかし最近の TA-Lib のバージョンの Python 用パッケージでは難なくインストールできることが判りました。

そこで今更ですが TA-Lib の使い方をおぼえようと、Jupyter Lab 上でテクニカル指標のいくつかをプロットしてみたので、備忘録的に内容をまとめました。

下記の OS 環境で動作確認をしています。

Fedora Linux Workstation 42 x86_64
Python 3.13.7
JupyterLab 4.4.7
matplotlib 3.10.6
mplfinance 0.12.10b0
numpy 2.3.3
pandas 2.3.2
ta-lib 0.6.7
yfinance 0.2.65

サンプル

ライブラリをインポート

最初に利用するライブラリをまとめてインポートします。

import matplotlib.font_manager as fm
import matplotlib.pyplot as plt
import mplfinance as mpf
import numpy as np
import pandas as pd
import yfinance as yf
from talib import BBANDS, MACD, MFI, MOM, OBV, RSI, SAR, STOCH

yfinance で日経平均株価指数の過去データを取得

サンプルとして、今年の 1 月から半年間の日足データを取得します。

symbol = "^N225"
ticker = yf.Ticker(symbol)
df = ticker.history(start="2025-01-01", end="2025-07-01", interval="1d")

サンプル期間より少し古いデータからも取得しておきます。これでテクニカル指標を算出して、サンプルの期間の最初から指標がプロットされるようにします。

df2 = ticker.history(start="2024-10-01", end="2025-07-01", interval="1d")

mplfinance でチャートを作成

サンプル期間の日足データをローソク足チャートと出来高の棒グラフを並べてプロットしました。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[3 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

mpf.plot(
    df,
    type="candle",
    style="default",
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
    volume=ax[1],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})")

plt.tight_layout()
# plt.savefig("screenshots/n225_default.png")
plt.show()

Bollinger bands

過去 20 日間の移動平均、移動標準偏差 +3σ, +2σ, +1σ, mean, -1σ, -2σ, -3σ でボリンジャーバンドを作成しました。

fig, ax = plt.subplots(figsize=(8, 3))

# BBANDS - Bollinger Bands
# upperband, middleband, lowerband = BBANDS(real, timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)
period = 20
mv_upper_1, mv_mean, mv_lower_1 = BBANDS(df2["Close"], period, 1, 1)
mv_upper_2, _, mv_lower_2 = BBANDS(df2["Close"], period, 2, 2)
mv_upper_3, _, mv_lower_3 = BBANDS(df2["Close"], period, 3, 3)

apds = [
    mpf.make_addplot(
        mv_upper_3[df.index],
        width=1,
        color="C0",
        linestyle="dotted",
        label="+3σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_upper_2[df.index],
        width=0.9,
        color="C1",
        linestyle="dashdot",
        label="+2σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_upper_1[df.index],
        width=0.75,
        color="C2",
        linestyle="dashed",
        label="+1σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_mean[df.index],
        width=1,
        color="C3",
        linestyle="solid",
        label="Mean",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_lower_1[df.index],
        width=0.75,
        color="C4",
        linestyle="dashed",
        label="-1σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_lower_2[df.index],
        width=0.9,
        color="C5",
        linestyle="dashdot",
        label="-2σ",
        ax=ax,
    ),
    mpf.make_addplot(
        mv_lower_3[df.index],
        width=1,
        color="C6",
        linestyle="dotted",
        label="-3σ",
        ax=ax,
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    update_width_config=dict(candle_linewidth=0.75),
    ax=ax,
)
ax.grid()
ax.legend(fontsize=7)
ax.set_title(
    f"{ticker.info['longName']} ({symbol})\nwith Bollinger Bands (period={period}days)"
)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_bbands.png")
plt.show()

Parabolic SAR

AF step=0.02, max=0.2 で Parabolic SAR をプロットしました。上昇下降トレンドの情報が無いので、灰色の丸点でプロットしました。

fig, ax = plt.subplots(figsize=(8, 3))

# SAR - Parabolic SAR
# real = SAR(high, low, acceleration=0, maximum=0)
af_step = 0.02
af_max = 0.2
sar = SAR(df2["High"], df2["Low"], af_step, af_max)

apds = [
    mpf.make_addplot(
        sar[df.index],
        type="scatter",
        marker='o',
        markersize=3,
        color="darkgray",
        ax=ax,
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    update_width_config=dict(candle_linewidth=0.75),
    ax=ax,
)
ax.grid()
ax.set_title(
    f"{ticker.info['longName']} ({symbol})\nwith Parabolic SAR (AF step={af_step}, max={af_max})"
)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_sar.png")
plt.show()

Momentum

過去 10 日間のデータでモメンタムを算出しました。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# MOM - Momentum
# real = MOM(real, timeperiod=10)
period = 10
mom = MOM(df2["Close"], period)
apds = [
    mpf.make_addplot(
        mom[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        ax=ax[1],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
)
ax[1].set_ylabel("Momentum")
ax[0].set_title(
    f"{ticker.info['longName']} ({symbol})\nwith Momentum (period={period}days)"
)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_mom.png")
plt.show()

RSI, Relative Strength Index

過去 14 日間のデータで RSI を算出しました。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# RSI - Relative Strength Index
# real = RSI(real, timeperiod=14)
period = 14
rsi = RSI(df2["Close"], period)
apds = [
    mpf.make_addplot(
        rsi[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        ax=ax[1],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})\nwith RSI (period={period}days)")
ax[1].set_ylabel("RSI")
ax[1].set_ylim(0, 100)
ax[1].axhline(30, color="black", linewidth=0.5)
ax[1].axhline(70, color="black", linewidth=0.5)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_rsi.png")
plt.show()

Stochastic oscillator

スローストキャスティクスをデフォルトのパラメータのままでプロットしています。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# STOCH - Stochastic
# slowk, slowd = STOCH(high, low, close, fastk_period=5, slowk_period=3, slowk_matype=0, slowd_period=3, slowd_matype=0)
slowk, slowd = STOCH(df2["High"], df2["Low"], df2["Close"])

apds = [
    mpf.make_addplot(
        slowk[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        label="Slow%K",
        ax=ax[1],
    ),
    mpf.make_addplot(
        slowd[df.index],
        width=1,
        color="C1",
        linestyle="solid",
        label="Slow%D",
        ax=ax[1],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})\nwith Stochastic oscillator")
ax[1].set_ylabel("Stochastic")
ax[1].set_ylim(0, 100)
ax[1].axhline(20, color="black", linewidth=0.5)
ax[1].axhline(80, color="black", linewidth=0.5)
ax[1].legend(fontsize=7)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_stoch.png")
plt.show()

MACD, Moving Average Convergence Divergence

MACD もデフォルトのパラメータでプロットしています。MACD のヒストグラムは正負で色を変えたかったのですが、すぐに出来なかったので単色にしてしまいました。

fig = plt.figure(figsize=(8, 4))
ax = dict()
n = 2
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# MACD - Moving Average Convergence/Divergence
# macd, macdsignal, macdhist = MACD(real, fastperiod=12, slowperiod=26, signalperiod=9)
period_fast = 12
period_slow = 26
period_signal = 9
macd, signal, macdhist = MACD(df2["Close"], period_fast, period_slow, period_signal)

apds = [
    mpf.make_addplot(
        macd[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        label="MACD",
        ax=ax[1],
    ),
    mpf.make_addplot(
        signal[df.index],
        width=1,
        color="C1",
        linestyle="solid",
        label="Signal",
        ax=ax[1],
    ),
    mpf.make_addplot(
        macdhist[df.index],
        type="bar",
        color="C2",
        ax=ax[1],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
)
ax[0].set_title(
    f"{ticker.info['longName']} ({symbol})\nwith MACD [{period_fast}, {period_slow}, {period_signal}]"
)
ax[1].set_ylabel("MACD")
ax[1].legend(fontsize=7)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_macd.png")
plt.show()

OBV, On Balance Volume

OBV は終値と出来高から算出する指標です。

fig = plt.figure(figsize=(8, 5))
ax = dict()
n = 3
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# OBV - On Balance Volume
# real = OBV(close, volume)
obv = OBV(df2["Close"], df2["Volume"])

apds = [
    mpf.make_addplot(
        obv[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        ax=ax[2],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
    volume=ax[1],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})\nwith OBV")
ax[2].set_ylabel("OBV")

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_obv.png")
plt.show()

MFI, Money Flow Index

MFI も、株価と出来高から算出する指標です。

fig = plt.figure(figsize=(8, 5))
ax = dict()
n = 3
gs = fig.add_gridspec(
    n, 1, wspace=0.0, hspace=0.0, height_ratios=[2 if i == 0 else 1 for i in range(n)]
)
for i, axis in enumerate(gs.subplots(sharex="col")):
    ax[i] = axis
    ax[i].grid()

# MFI - Money Flow Index
# NOTE: The MFI function has an unstable period.
# real = MFI(high, low, close, volume, timeperiod=14)
period = 14
mfi = MFI(df2["High"], df2["Low"], df2["Close"], df2["Volume"], period)

apds = [
    mpf.make_addplot(
        mfi[df.index],
        width=1,
        color="C0",
        linestyle="solid",
        ax=ax[2],
    ),
]
mpf.plot(
    df,
    type="candle",
    style="default",
    addplot=apds,
    datetime_format="%m/%d",
    xrotation=0,
    ax=ax[0],
    volume=ax[1],
)
ax[0].set_title(f"{ticker.info['longName']} ({symbol})\nwith MFI (period={period}days)")
ax[2].axhline(20, color="black", linewidth=0.5)
ax[2].axhline(80, color="black", linewidth=0.5)
ax[2].set_ylabel("MFI")
ax[2].set_ylim(0, 100)

plt.tight_layout()
# plt.savefig("screenshots/n225_talib_mfi.png")
plt.show()

参考サイト

  1. TA-Lib - Technical Analysis Library
  2. TA-Lib/ta-lib: TA-Lib (Core C Library)
  3. TA-Lib/ta-lib-python: Python wrapper for TA-Lib
  4. TA-Lib · PyPI

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ オープンソースへ
にほんブログ村

オープンソース - ブログ村ハッシュタグ
#オープンソース



このエントリーをはてなブックマークに追加