株価データの取得と分析
株価データを取得してテクニカル分析を実装し、投資判断をサポートする可視化を行います。
この記事で学べること
- Yahoo Financeからの株価取得
- テクニカル指標の計算
- ローソク足チャートの作成
- 複数銘柄の比較分析
1. 準備:必要なライブラリ
# インストール
pip install yfinance
pip install pandas
pip install matplotlib
pip install mplfinance
pip install ta
pip install plotly
pip install requestsrequirements.txt
yfinance==0.2.31
pandas==2.1.0
matplotlib==3.8.0
mplfinance==0.12.10b0
ta==0.11.0
plotly==5.17.0
requests==2.31.02. 基本的な株価取得
stock_scraper.py
#!/usr/bin/env python3
"""株価データスクレイパー"""
import yfinance as yf
import pandas as pd
from datetime import datetime, timedelta
class StockScraper:
def __init__(self):
pass
def get_stock_data(self, ticker, period='1y', interval='1d'):
"""株価データを取得
Parameters:
-----------
ticker : str
銘柄コード (例: 'AAPL', '7203.T')
period : str
期間 ('1d', '5d', '1mo', '3mo', '6mo', '1y', '2y', '5y', 'max')
interval : str
間隔 ('1m', '2m', '5m', '15m', '30m', '60m', '1d', '1wk', '1mo')
"""
try:
stock = yf.Ticker(ticker)
df = stock.history(period=period, interval=interval)
if df.empty:
print(f"データが見つかりません: {ticker}")
return None
# インデックスをリセット
df.reset_index(inplace=True)
print(f"✅ {ticker}: {len(df)}件のデータを取得")
return df
except Exception as e:
print(f"❌ エラー: {e}")
return None
def get_stock_info(self, ticker):
"""銘柄情報を取得"""
try:
stock = yf.Ticker(ticker)
info = stock.info
data = {
'銘柄名': info.get('longName', 'N/A'),
'業種': info.get('sector', 'N/A'),
'現在価格': info.get('currentPrice', 'N/A'),
'時価総額': info.get('marketCap', 'N/A'),
'PER': info.get('trailingPE', 'N/A'),
'PBR': info.get('priceToBook', 'N/A'),
'配当利回り': info.get('dividendYield', 'N/A'),
'52週高値': info.get('fiftyTwoWeekHigh', 'N/A'),
'52週安値': info.get('fiftyTwoWeekLow', 'N/A')
}
return data
except Exception as e:
print(f"❌ エラー: {e}")
return None
def get_multiple_stocks(self, tickers, period='1y'):
"""複数銘柄のデータを取得"""
all_data = {}
for ticker in tickers:
df = self.get_stock_data(ticker, period=period)
if df is not None:
all_data[ticker] = df
return all_data
# 使用例
if __name__ == '__main__':
scraper = StockScraper()
# 米国株(Apple)
df = scraper.get_stock_data('AAPL', period='1y')
print(f"\nデータ期間: {df['Date'].min()} ~ {df['Date'].max()}")
print(f"最新終値: ${df['Close'].iloc[-1]:.2f}")
# 日本株(トヨタ)
df_toyota = scraper.get_stock_data('7203.T', period='6mo')
# 銘柄情報
info = scraper.get_stock_info('AAPL')
print("\n=== Apple株情報 ===")
for key, value in info.items():
print(f"{key}: {value}")実行結果
✅ AAPL: 252件のデータを取得
データ期間: 2024-10-26 ~ 2025-10-25
最新終値: $178.45
=== Apple株情報 ===
銘柄名: Apple Inc.
業種: Technology
現在価格: 178.45
時価総額: 2750000000000
PER: 29.3
PBR: 45.2
配当利回り: 0.0048
52週高値: 199.62
52週安値: 164.083. テクニカル指標の計算
technical_analysis.py
#!/usr/bin/env python3
"""テクニカル分析指標"""
import pandas as pd
import numpy as np
from ta.trend import SMAIndicator, EMAIndicator, MACD
from ta.momentum import RSIIndicator, StochasticOscillator
from ta.volatility import BollingerBands
class TechnicalAnalyzer:
def __init__(self, df):
"""データフレームを初期化"""
self.df = df.copy()
def add_moving_averages(self, periods=[5, 25, 75]):
"""移動平均線を追加"""
for period in periods:
# 単純移動平均(SMA)
sma = SMAIndicator(close=self.df['Close'], window=period)
self.df[f'SMA_{period}'] = sma.sma_indicator()
# 指数移動平均(EMA)
ema = EMAIndicator(close=self.df['Close'], window=period)
self.df[f'EMA_{period}'] = ema.ema_indicator()
return self.df
def add_rsi(self, period=14):
"""RSI(相対力指数)を追加"""
rsi = RSIIndicator(close=self.df['Close'], window=period)
self.df['RSI'] = rsi.rsi()
return self.df
def add_macd(self):
"""MACD(移動平均収束拡散)を追加"""
macd = MACD(
close=self.df['Close'],
window_slow=26,
window_fast=12,
window_sign=9
)
self.df['MACD'] = macd.macd()
self.df['MACD_Signal'] = macd.macd_signal()
self.df['MACD_Histogram'] = macd.macd_diff()
return self.df
def add_bollinger_bands(self, period=20):
"""ボリンジャーバンドを追加"""
bb = BollingerBands(
close=self.df['Close'],
window=period,
window_dev=2
)
self.df['BB_Upper'] = bb.bollinger_hband()
self.df['BB_Middle'] = bb.bollinger_mavg()
self.df['BB_Lower'] = bb.bollinger_lband()
return self.df
def add_stochastic(self, period=14):
"""ストキャスティクスを追加"""
stoch = StochasticOscillator(
high=self.df['High'],
low=self.df['Low'],
close=self.df['Close'],
window=period
)
self.df['Stoch_K'] = stoch.stoch()
self.df['Stoch_D'] = stoch.stoch_signal()
return self.df
def add_all_indicators(self):
"""すべての指標を追加"""
self.add_moving_averages()
self.add_rsi()
self.add_macd()
self.add_bollinger_bands()
self.add_stochastic()
return self.df
def get_signals(self):
"""売買シグナルを生成"""
signals = []
latest = self.df.iloc[-1]
# RSIシグナル
if latest['RSI'] < 30:
signals.append('🟢 RSI: 買われすぎ(買いシグナル)')
elif latest['RSI'] > 70:
signals.append('🔴 RSI: 売られすぎ(売りシグナル)')
# MACDシグナル
if latest['MACD'] > latest['MACD_Signal']:
signals.append('🟢 MACD: ゴールデンクロス(買い)')
elif latest['MACD'] < latest['MACD_Signal']:
signals.append('🔴 MACD: デッドクロス(売り)')
# ボリンジャーバンド
if latest['Close'] < latest['BB_Lower']:
signals.append('🟢 BB: 下限接触(反発期待)')
elif latest['Close'] > latest['BB_Upper']:
signals.append('🔴 BB: 上限接触(調整期待)')
# 移動平均線
if latest['Close'] > latest['SMA_25']:
signals.append('🟢 SMA25: 上抜け(上昇トレンド)')
else:
signals.append('🔴 SMA25: 下抜け(下降トレンド)')
return signals
# 使用例
from stock_scraper import StockScraper
scraper = StockScraper()
df = scraper.get_stock_data('AAPL', period='6mo')
analyzer = TechnicalAnalyzer(df)
df = analyzer.add_all_indicators()
print("\n=== 最新の指標値 ===")
latest = df.iloc[-1]
print(f"終値: ${latest['Close']:.2f}")
print(f"RSI: {latest['RSI']:.2f}")
print(f"MACD: {latest['MACD']:.2f}")
print(f"SMA25: ${latest['SMA_25']:.2f}")
print("\n=== 売買シグナル ===")
for signal in analyzer.get_signals():
print(signal)4. ローソク足チャート作成
visualizer.py
#!/usr/bin/env python3
"""株価データ可視化"""
import mplfinance as mpf
import matplotlib.pyplot as plt
import pandas as pd
class StockVisualizer:
def __init__(self, df):
self.df = df.copy()
# DateをDatetimeIndexに変換
if 'Date' in self.df.columns:
self.df.set_index('Date', inplace=True)
def plot_candlestick(self, ticker='Stock', save_path=None):
"""ローソク足チャート作成"""
# 移動平均線を追加プロット
add_plots = []
if 'SMA_25' in self.df.columns:
add_plots.append(
mpf.make_addplot(self.df['SMA_25'], color='blue', width=1.5)
)
if 'SMA_75' in self.df.columns:
add_plots.append(
mpf.make_addplot(self.df['SMA_75'], color='red', width=1.5)
)
# スタイル設定
mc = mpf.make_marketcolors(
up='#17BF63',
down='#F91880',
edge='inherit',
wick='inherit',
volume='in'
)
s = mpf.make_mpf_style(
marketcolors=mc,
gridstyle='-',
gridcolor='#E0E0E0',
facecolor='white',
figcolor='white'
)
# プロット
kwargs = dict(
type='candle',
style=s,
title=f'{ticker} 株価チャート',
ylabel='価格 ($)',
volume=True,
ylabel_lower='出来高',
figsize=(14, 8)
)
if add_plots:
kwargs['addplot'] = add_plots
if save_path:
kwargs['savefig'] = save_path
mpf.plot(self.df, **kwargs)
def plot_technical_indicators(self, ticker='Stock'):
"""テクニカル指標のグラフ"""
fig, axes = plt.subplots(4, 1, figsize=(14, 12), sharex=True)
# 1. 価格とボリンジャーバンド
axes[0].plot(self.df.index, self.df['Close'], label='終値', linewidth=2, color='#000')
if 'BB_Upper' in self.df.columns:
axes[0].plot(self.df.index, self.df['BB_Upper'], 'r--', alpha=0.5, label='上限')
axes[0].plot(self.df.index, self.df['BB_Middle'], 'b-', alpha=0.5, label='中央')
axes[0].plot(self.df.index, self.df['BB_Lower'], 'r--', alpha=0.5, label='下限')
axes[0].fill_between(
self.df.index,
self.df['BB_Lower'],
self.df['BB_Upper'],
alpha=0.1,
color='gray'
)
axes[0].set_ylabel('価格 ($)', fontsize=11)
axes[0].set_title(f'{ticker} テクニカル分析', fontsize=14, fontweight='bold')
axes[0].legend(loc='upper left')
axes[0].grid(True, alpha=0.3)
# 2. RSI
if 'RSI' in self.df.columns:
axes[1].plot(self.df.index, self.df['RSI'], color='#9C27B0', linewidth=2)
axes[1].axhline(y=70, color='r', linestyle='--', alpha=0.5, label='売られすぎ')
axes[1].axhline(y=30, color='g', linestyle='--', alpha=0.5, label='買われすぎ')
axes[1].fill_between(self.df.index, 30, 70, alpha=0.1, color='gray')
axes[1].set_ylabel('RSI', fontsize=11)
axes[1].set_ylim(0, 100)
axes[1].legend(loc='upper left')
axes[1].grid(True, alpha=0.3)
# 3. MACD
if 'MACD' in self.df.columns:
axes[2].plot(self.df.index, self.df['MACD'], label='MACD', linewidth=2, color='#2196F3')
axes[2].plot(self.df.index, self.df['MACD_Signal'], label='シグナル', linewidth=2, color='#FF9800')
axes[2].bar(
self.df.index,
self.df['MACD_Histogram'],
label='ヒストグラム',
color=['g' if val >= 0 else 'r' for val in self.df['MACD_Histogram']],
alpha=0.3
)
axes[2].axhline(y=0, color='black', linestyle='-', linewidth=0.5)
axes[2].set_ylabel('MACD', fontsize=11)
axes[2].legend(loc='upper left')
axes[2].grid(True, alpha=0.3)
# 4. 出来高
colors = ['g' if close >= open_ else 'r'
for close, open_ in zip(self.df['Close'], self.df['Open'])]
axes[3].bar(self.df.index, self.df['Volume'], color=colors, alpha=0.5)
axes[3].set_ylabel('出来高', fontsize=11)
axes[3].set_xlabel('日付', fontsize=11)
axes[3].grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig(f'{ticker}_technical.png', dpi=300, bbox_inches='tight')
plt.show()
def plot_comparison(self, other_dfs, labels):
"""複数銘柄の比較"""
plt.figure(figsize=(14, 7))
# 最初の日を基準に正規化
normalized = (self.df['Close'] / self.df['Close'].iloc[0] - 1) * 100
plt.plot(self.df.index, normalized, linewidth=2, label=labels[0])
for df, label in zip(other_dfs, labels[1:]):
if 'Date' in df.columns:
df.set_index('Date', inplace=True)
normalized = (df['Close'] / df['Close'].iloc[0] - 1) * 100
plt.plot(df.index, normalized, linewidth=2, label=label)
plt.xlabel('日付', fontsize=12)
plt.ylabel('変化率 (%)', fontsize=12)
plt.title('銘柄比較(騰落率)', fontsize=14, fontweight='bold')
plt.legend()
plt.grid(True, alpha=0.3)
plt.axhline(y=0, color='black', linestyle='-', linewidth=0.8)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('stock_comparison.png', dpi=300, bbox_inches='tight')
plt.show()使用例
from stock_scraper import StockScraper
from technical_analysis import TechnicalAnalyzer
from visualizer import StockVisualizer
# データ取得
scraper = StockScraper()
df = scraper.get_stock_data('AAPL', period='1y')
# テクニカル指標追加
analyzer = TechnicalAnalyzer(df)
df = analyzer.add_all_indicators()
# グラフ作成
visualizer = StockVisualizer(df)
visualizer.plot_candlestick('AAPL', save_path='AAPL_candlestick.png')
visualizer.plot_technical_indicators('AAPL')5. 複数銘柄の比較分析
def compare_stocks(tickers, period='1y'):
"""複数銘柄を比較分析"""
scraper = StockScraper()
all_data = scraper.get_multiple_stocks(tickers, period=period)
if not all_data:
print("データ取得失敗")
return
# 統計サマリー
print("\n=== 銘柄比較サマリー ===")
summary = []
for ticker, df in all_data.items():
start_price = df['Close'].iloc[0]
end_price = df['Close'].iloc[-1]
change = ((end_price - start_price) / start_price) * 100
volatility = df['Close'].pct_change().std() * 100
summary.append({
'銘柄': ticker,
'開始価格': f'${start_price:.2f}',
'最新価格': f'${end_price:.2f}',
'騰落率': f'{change:+.2f}%',
'ボラティリティ': f'{volatility:.2f}%',
'最高値': f'${df["High"].max():.2f}',
'最安値': f'${df["Low"].min():.2f}'
})
summary_df = pd.DataFrame(summary)
print(summary_df.to_string(index=False))
# 比較グラフ
first_ticker = list(all_data.keys())[0]
first_df = all_data[first_ticker]
other_dfs = [all_data[t] for t in list(all_data.keys())[1:]]
visualizer = StockVisualizer(first_df)
visualizer.plot_comparison(other_dfs, tickers)
# 使用例:GAFAM比較
tickers = ['GOOGL', 'AAPL', 'META', 'AMZN', 'MSFT']
compare_stocks(tickers, period='1y')6. バックテスト機能
class SimpleBacktest:
"""シンプルな売買戦略バックテスト"""
def __init__(self, df, initial_capital=10000):
self.df = df.copy()
self.initial_capital = initial_capital
self.capital = initial_capital
self.position = 0
self.trades = []
def moving_average_crossover(self, fast=25, slow=75):
"""移動平均クロス戦略"""
self.df['signal'] = 0
# ゴールデンクロス(買い)
self.df.loc[
(self.df[f'SMA_{fast}'] > self.df[f'SMA_{slow}']) &
(self.df[f'SMA_{fast}'].shift(1) <= self.df[f'SMA_{slow}'].shift(1)),
'signal'
] = 1
# デッドクロス(売り)
self.df.loc[
(self.df[f'SMA_{fast}'] < self.df[f'SMA_{slow}']) &
(self.df[f'SMA_{fast}'].shift(1) >= self.df[f'SMA_{slow}'].shift(1)),
'signal'
] = -1
return self.execute_trades()
def execute_trades(self):
"""取引を実行"""
for idx, row in self.df.iterrows():
if row['signal'] == 1 and self.position == 0:
# 買い
shares = self.capital // row['Close']
if shares > 0:
self.position = shares
cost = shares * row['Close']
self.capital -= cost
self.trades.append({
'date': row['Date'],
'action': '買い',
'price': row['Close'],
'shares': shares,
'value': cost
})
elif row['signal'] == -1 and self.position > 0:
# 売り
proceeds = self.position * row['Close']
self.capital += proceeds
self.trades.append({
'date': row['Date'],
'action': '売り',
'price': row['Close'],
'shares': self.position,
'value': proceeds
})
self.position = 0
# 最終ポジションを清算
if self.position > 0:
final_value = self.position * self.df['Close'].iloc[-1]
self.capital += final_value
return self.get_performance()
def get_performance(self):
"""パフォーマンス計算"""
total_return = ((self.capital - self.initial_capital) /
self.initial_capital * 100)
print("\n=== バックテスト結果 ===")
print(f"初期資金: ${self.initial_capital:,.2f}")
print(f"最終資金: ${self.capital:,.2f}")
print(f"総リターン: {total_return:+.2f}%")
print(f"取引回数: {len(self.trades)}")
return pd.DataFrame(self.trades)
# 使用例
from stock_scraper import StockScraper
from technical_analysis import TechnicalAnalyzer
scraper = StockScraper()
df = scraper.get_stock_data('AAPL', period='2y')
analyzer = TechnicalAnalyzer(df)
df = analyzer.add_moving_averages([25, 75])
backtest = SimpleBacktest(df, initial_capital=10000)
trades_df = backtest.moving_average_crossover(fast=25, slow=75)
print("\n=== 取引履歴 ===")
print(trades_df)7. リアルタイム監視
import time
from datetime import datetime
def monitor_stocks(tickers, interval=60, threshold=2.0):
"""株価をリアルタイム監視
Parameters:
-----------
tickers : list
監視する銘柄リスト
interval : int
更新間隔(秒)
threshold : float
アラート閾値(変化率%)
"""
scraper = StockScraper()
previous_prices = {}
print(f"\n=== 株価監視開始 ===")
print(f"銘柄: {', '.join(tickers)}")
print(f"更新間隔: {interval}秒")
print(f"アラート閾値: ±{threshold}%\n")
try:
while True:
current_time = datetime.now().strftime('%H:%M:%S')
print(f"\n[{current_time}] 更新中...")
for ticker in tickers:
df = scraper.get_stock_data(ticker, period='1d', interval='1m')
if df is not None and len(df) > 0:
current_price = df['Close'].iloc[-1]
if ticker in previous_prices:
change = ((current_price - previous_prices[ticker]) /
previous_prices[ticker] * 100)
status = '📈' if change > 0 else '📉'
print(f"{status} {ticker}: ${current_price:.2f} ({change:+.2f}%)")
# アラート
if abs(change) >= threshold:
alert = '🚨 大きな変動!' if abs(change) >= threshold * 2 else '⚠️ 注意'
print(f" {alert} {abs(change):.2f}%の変動を検出")
else:
print(f"📊 {ticker}: ${current_price:.2f}")
previous_prices[ticker] = current_price
time.sleep(interval)
except KeyboardInterrupt:
print("\n\n監視を終了します")
# 使用例(Ctrl+Cで停止)
# monitor_stocks(['AAPL', 'GOOGL', 'MSFT'], interval=300, threshold=1.5)8. 日本株対応
# 日本株の銘柄コード
japanese_stocks = {
'7203.T': 'トヨタ自動車',
'9984.T': 'ソフトバンクグループ',
'6758.T': 'ソニーグループ',
'7974.T': '任天堂',
'6501.T': '日立製作所',
'8306.T': '三菱UFJ',
'9432.T': 'NTT',
'6861.T': 'キーエンス'
}
def analyze_japanese_market():
"""日本株市場を分析"""
scraper = StockScraper()
print("\n=== 日本株分析 ===")
results = []
for ticker, name in japanese_stocks.items():
df = scraper.get_stock_data(ticker, period='3mo')
if df is not None:
analyzer = TechnicalAnalyzer(df)
df = analyzer.add_all_indicators()
latest = df.iloc[-1]
change = ((latest['Close'] - df['Close'].iloc[0]) /
df['Close'].iloc[0] * 100)
results.append({
'銘柄名': name,
'コード': ticker,
'終値': f'¥{latest["Close"]:,.0f}',
'3ヶ月騰落': f'{change:+.2f}%',
'RSI': f'{latest["RSI"]:.1f}',
'売買判断': '買い' if latest['RSI'] < 40 else '売り' if latest['RSI'] > 60 else '中立'
})
results_df = pd.DataFrame(results)
print(results_df.to_string(index=False))
return results_df
# 実行
analyze_japanese_market()まとめ
yfinanceで株価データを取得・分析できます!
重要ポイント
- ✅ yfinanceは無料で使いやすい
- ✅ テクニカル指標でトレンド把握
- ✅ mplfinanceで美しいチャート作成
- ✅ バックテストで戦略検証
- ✅ 日本株にも対応
応用例
- 自動売買システム構築
- ポートフォリオ管理
- アービトラージ検出
- リスク管理ダッシュボード
- 経済指標との相関分析
注意事項
- ⚠️ 投資は自己責任
- ⚠️ データの正確性を確認
- ⚠️ リアルタイムデータには遅延あり
- ⚠️ 過去の成績は将来を保証しない