乙Py先生のプログラミング教室

乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

今回はPandas_Datareaderを使って
日本の株価を取得して分析する方法についてです。

解説動画はこちら


 
始めに


最近、日本の株価がバブル期並みの
高音を付けている様です。

この際
Pythonで株価のデータ分析が行える様に
なってみるのも面白いかと思います。

日本の株価を取得するには
pandas_datareaderというものを使います。

Google Colabであれば
最初から使えるので、こちらを使っていきましょう。


pandas_datareader


様々な金融商品のデータを取得する事ができます
その中でも、日本の株価は「stooq」というもので
ポーランドかどこかの会社のデータの様です。

こちらを取得して分析を始めていきましょう。


基本的な使い方はこのようになっています。
import pandas_datareader.data as web

web.DataReader(銘柄コード, data_source='stooq', start=開始日,end=終了日)
株価は銘柄ごとのデータになっているので
銘柄コードを指定して取得します。

データを取得するコードはこちら
# ライブラリのインポート
import os
import datetime as dt
import pandas_datareader.data as web
import plotly.graph_objects as go
import pandas as pd

# 銘柄コード入力(7203はトヨタ自動車)
code = "7203"
symbol = code + ".JP"

# 2023-01-01以降の株価取得
start , end = '2023-01-01', dt.date.today()

#データ取得
df = web.DataReader(symbol, data_source='stooq', start=start,end=end)
df.insert(0, "code", code, allow_duplicates=False)
df = df.sort_index()
df.tail(3)
スクリーンショット 2024-02-17 17.28.58

銘柄を変えたい場合は
codeの数字を変えて下さい。



株価から指標を計算してみる


ここからは取得したデータを使って
株価の分析に必要な指標を計算していきます。


移動平均
過去X日間の価格の平均値です
通常は終値を使います。


ボリンジャーバンド

移動平均を表す線と
その上下に値動きの幅を示す線を加えた指標で
「価格の大半がこの帯(バンド)の中に収まる」という
統計学を応用したテクニカル指標のひとつです。

+-1-3@までが一般的に良く用いられます。
@は標準偏差の様な意味合いです。

移動平均などの指標の計算
# 移動平均を計算する期間
period = 20

# 移動平均を計算
df[f'MA{period}'] = df['Close'].rolling(window=period).mean()

# ボリンジャーバンドを計算
std = df['Close'].rolling(window=period).std()
df['Upper Band1'] = df[f'MA{period}'] + 1 * std
df['Upper Band2'] = df[f'MA{period}'] + 2 * std
df['Upper Band3'] = df[f'MA{period}'] + 3 * std
df['Lower Band1'] = df[f'MA{period}'] - 1 * std
df['Lower Band2'] = df[f'MA{period}'] - 2 * std
df['Lower Band3'] = df[f'MA{period}'] - 3 * std

指標が計算できたら描画してみましょう。
描画には「Plotly」を用います。

これはライブで操作が出来る描画ライブラリで
静止画ではないので、色々な操作が行え捗ります。

# プロット用のデータを作成
data = [
    go.Candlestick(
        x=df.index,
        open=df['Open'],
        high=df['High'],
        low=df['Low'],
        close=df['Close'],
        name='株価',
    ),
    go.Scatter(x=df.index,y=df['Upper Band2'],mode='lines',name='+2@',),
    go.Scatter(x=df.index,y=df['Upper Band1'],mode='lines',name='+1@',),
    go.Scatter(x=df.index,y=df[f'MA{period}'],mode='lines',name='移動平均',),
    go.Scatter(x=df.index,y=df['Lower Band2'],mode='lines',name='-2@',),
    go.Scatter(x=df.index,y=df['Lower Band1'],mode='lines',name='-1@',),
]

# レイアウト設定
layout = go.Layout(
    title='株価の移動平均とボリンジャーバンド',
    xaxis=dict(
        title='日付',
        tickformat='%Y-%m-%d'
    ),
    yaxis=dict(title='株価'),
)

# グラフを描画
fig = go.Figure(data=data, layout=layout)
fig.show()
スクリーンショット 2024-02-17 17.29.13



ここからはテクニカル指標を
計算して描画していきましょう。
# RSIを計算する関数
def calculate_rsi(data, window=14):
    delta = data.diff()
    up = delta.copy()
    down = delta.copy()
    up[up < 0] = 0
    down[down > 0] = 0
    avg_gain = up.rolling(window).mean()
    avg_loss = abs(down.rolling(window).mean())
    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    return rsi

# ストキャスティクスを計算する関数
def calculate_stochastic_oscillator(data, window=14):
    lowest_low = data.rolling(window).min()
    highest_high = data.rolling(window).max()
    stochastic_oscillator = (data - lowest_low) / (highest_high - lowest_low) * 100
    return stochastic_oscillator

# MACDを計算する関数
def calculate_macd(data, short_window=12, long_window=26, signal_window=9):
    short_ema = data.ewm(span=short_window, adjust=False).mean()
    long_ema = data.ewm(span=long_window, adjust=False).mean()
    macd = short_ema - long_ema
    signal_line = macd.ewm(span=signal_window, adjust=False).mean()
    histogram = macd - signal_line
    return macd, signal_line, histogram

# RSIを計算
df['RSI'] = calculate_rsi(df['Close'])

# ストキャスティクスを計算
df['%K'] = calculate_stochastic_oscillator(df['Close'])
df['%D'] = df['%K'].rolling(3).mean()

# MACDを計算
macd, signal_line, histogram = calculate_macd(df['Close'])
df['MACD'] = macd
df['Signal'] = signal_line
df['Histogram'] = histogram



MACD(移動平均収束拡散手法)
短期の移動平均線と中長期の移動平均線を使用することで
買いと売りを判断する手法です。

MACDは基本となる線(MACD線)
MACDの移動平均線である
シグナル線の推移でマーケットを判断

MACD線がシグナル線を上抜けると
ゴールデンクロス(買いタイミング)

MACD線がシグナル線を下抜けると
デッドクロス(売りタイミング)


# MACD
fig = go.Figure(
        data=[
                go.Candlestick(
                    x=df.index,
                    open=df['Open'],
                    high=df['High'],
                    low=df['Low'],
                    close=df['Close'],
                    name='株価',
                    yaxis='y1'
                ),
                go.Scatter(x=df.index, y=df['MACD'], line=dict(color='red', width=1), name="macd", yaxis='y2'),
                go.Scatter(x=df.index, y=df['Signal'], line=dict(color='blue', width=1), name="signal", yaxis='y2')
            ],
        layout = go.Layout(
                xaxis=dict(
                    title='日付',
                    tickformat='%Y-%m-%d'
                ),
                yaxis = dict(title='株価', side='left', showgrid=False, range=[df.Low.min(), df.High.max()]),
                yaxis2 = dict(title='指標', side='right', overlaying='y',
                              range=[min(df.Signal.min(), df.MACD.min()),
                              max(df.MACD.max(), df.Signal.max())]
                              )
        )
)
fig.show()
スクリーンショット 2024-02-17 17.29.28


RSI
「Relative Strength Index」の略で
日本語に訳すと「相対力指数」
買われすぎか、売られすぎかを判断するための指標です。

過去一定期間の上げ幅(前日比)の合計を
同じ期間の上げ幅の合計と下げ幅の合計を足した数字で割って
100を掛けたもの

一般的に70~80%以上で買われすぎ
20~30%以下で売られすぎと判断します。

# RSI
fig = go.Figure(
        data=[
                go.Candlestick(
                    x=df.index,
                    open=df['Open'],
                    high=df['High'],
                    low=df['Low'],
                    close=df['Close'],
                    name='株価',
                    yaxis='y1'
                ),
                go.Scatter(x=df.index, y=df['RSI'], line=dict(color='blue', width=1), name="RSI", yaxis='y2'),
            ],
        layout = go.Layout(
                xaxis=dict(
                    title='日付',
                    tickformat='%Y-%m-%d'
                ),
                yaxis = dict(title='株価', side='left', showgrid=False, range=[df.Low.min(), df.High.max()]),
                yaxis2 = dict(title='指標', side='right', overlaying='y',
                              range=[df.RSI.min(), df.RSI.max()]
                              )
        )
)
fig.show()
スクリーンショット 2024-02-17 17.29.38

ストキャスティクス

チャートは%K(Fast)と%D(Slow)の2本の線で表され
数値は0%から100%の範囲で推移します。

%Kラインは相場に対して敏感に動き
Dラインは%Kラインよりも遅く動きますが
より重要とされているのは%Dラインです。

一般的には20~30%以下で売られすぎ
70~80%以上で買われすぎと判断しますが
相場の勢いが強い場合には
20%や80%を突破することもあります。


# ストキャスティクス
fig = go.Figure(
        data=[
                go.Candlestick(
                    x=df.index,
                    open=df['Open'],
                    high=df['High'],
                    low=df['Low'],
                    close=df['Close'],
                    name='株価',
                    yaxis='y1'
                ),
                go.Scatter(x=df.index, y=df['%K'], line=dict(color='blue', width=1), name="%K", yaxis='y2'),
                go.Scatter(x=df.index, y=df['%D'], line=dict(color='red', width=1), name="%D", yaxis='y2')
            ],
        layout = go.Layout(
                xaxis=dict(
                    title='日付',
                    tickformat='%Y-%m-%d'
                ),
                yaxis = dict(title='株価', side='left', showgrid=False, range=[df.Low.min(), df.High.max()]),
                yaxis2 = dict(title='指標', side='right', overlaying='y',
                              range=[min(df['%K'].min(), df['%D'].min()),
                              max(df['%K'].max(), df['%D'].max())]
                              )
        )
)
fig.show()
スクリーンショット 2024-02-17 17.29.49


トレードシミュレーション

ここからは株価と指標を用いて
売買のシミュレーションを行ってみましょう。

MACDでエントリーポイントを決めて売買する
簡単なシミュレーションを行ってみます。

# トレードのシミュレーションを行う関数
def simulate_trades(df):
    positions = []  # トレードのポジション(買い or 売り)
    entry_prices = []  # エントリー価格
    exit_prices = []  # エグジット価格
    pnl = []  # 損益

    for i in range(len(df)):
        if i == 0:
            positions.append(None)
            entry_prices.append(None)
            exit_prices.append(None)
            pnl.append(None)
        else:
            if df['MACD'][i] > df['Signal'][i] and df['MACD'][i-1] <= df['Signal'][i-1]:
                # MACDがシグナルを上回った時に買いポジション
                positions.append('Buy')
                entry_prices.append(df['Close'][i])
                exit_prices.append(None)
                pnl.append(None)
            elif df['MACD'][i] < df['Signal'][i] and df['MACD'][i-1] >= df['Signal'][i-1]:
                # MACDがシグナルを下回った時に売りポジション
                positions.append('Sell')
                entry_prices.append(df['Close'][i])
                exit_prices.append(None)
                pnl.append(None)
            elif positions[-1] == 'Buy':
                # 買いポジションを保持している場合
                positions.append('Buy')
                entry_prices.append(entry_prices[-1])
                exit_prices.append(df['Close'][i])
                pnl.append(df['Close'][i] - entry_prices[-1])
            elif positions[-1] == 'Sell':
                # 売りポジションを保持している場合
                positions.append('Sell')
                entry_prices.append(entry_prices[-1])
                exit_prices.append(df['Close'][i])
                pnl.append(entry_prices[-1] - df['Close'][i])
            else:
                positions.append(None)
                entry_prices.append(None)
                exit_prices.append(None)
                pnl.append(None)

    df['Position'] = positions
    df['Entry Price'] = entry_prices
    df['Exit Price'] = exit_prices
    df['P&L'] = pnl
    return df

# トレードのシミュレーションを実行
df = simulate_trades(df)

結果を描画してみると

# プロット用のデータを作成
buy_indices = df[df['Position'] == 'Buy'].index
sell_indices = df[df['Position'] == 'Sell'].index

fig = go.Figure()

# Closeをプロット
fig.add_trace(go.Scatter(x=df.index, y=df['Close'], name='Close', mode='lines'))

# Entry Priceをプロット
fig.add_trace(go.Scatter(x=df.loc[buy_indices].index, y=df.loc[buy_indices]['Entry Price'],
                         name='Entry Price (BUY)', mode='markers', marker=dict(color='blue', size=8)))
fig.add_trace(go.Scatter(x=df.loc[sell_indices].index, y=df.loc[sell_indices]['Entry Price'],
                         name='Entry Price (SELL)', mode='markers', marker=dict(color='red', size=8)))

# Exit Priceをプロット
fig.add_trace(go.Scatter(x=df.loc[buy_indices].index, y=df.loc[buy_indices]['Exit Price'],
                         name='Exit Price (BUY)', mode='markers', marker=dict(color='blue', size=8, symbol='triangle-up')))
fig.add_trace(go.Scatter(x=df.loc[sell_indices].index, y=df.loc[sell_indices]['Exit Price'],
                         name='Exit Price (SELL)', mode='markers', marker=dict(color='red', size=8, symbol='triangle-down')))

# P&Lをプロット(二番目の軸)
fig.add_trace(go.Scatter(x=df.index, y=df['P&L'], name='P&L', mode='lines', yaxis='y2'))

# レイアウト設定
fig.update_layout(title='Trading Simulation',
                  xaxis_title='Date',
                  yaxis=dict(title='Price', side='left'),
                  yaxis2=dict(title='P&L', side='right', overlaying='y', showgrid=False),
                  legend=dict(x=0, y=1),
                  hovermode='x unified')

# グラフの表示
fig.show()
スクリーンショット 2024-02-17 17.28.45

こんな感じで、売買ポイントの可視化が出来ました。

今回は簡易なデータ取得と
分析手法になっているので
気軽に始められるかと思います。

色々試してみて下さいね
それでは。


今回は迷路の自販機を
再現するコードの実装です。

解説動画はこちら




迷路の自販機

先日、テレビで迷路の自販機なるものを
特集していました。


こんな感じのものですね

お金を入れるとその分の長さの
迷路が生成されて
プリントされるというものです。

高いほど長くなる仕組みの様です。

迷路を自動生成する事ができれば
これを再現する事ができるんじゃ
ないかなと思います。

早速コードを見てみましょう。


迷路作成コード

迷路を作成する部分と
画像を生成する部分です。
import random
import sys
from PIL import Image
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
sys.setrecursionlimit(10**6)

# 迷路を作成する
def make_maze(size):

    def make(ny, nx,tmp_maze):
        ar = list(range(4)) 
        random.shuffle(ar)
        for i in ar:
            if ny+dy[i][1]<1 or ny+dy[i][1]>=size[0]:
                continue
            if nx+dx[i][1]<1 or nx+dx[i][1]>=size[1]:
                continue
            if tmp_maze[ny+dy[i][1]][nx+dx[i][1]]==0:
                continue
            for j in range(2):
                tmp_maze[ny+dy[i][j]][nx+dx[i][j]]=0
            make(ny+dy[i][1], nx+dx[i][1],tmp_maze)
    
    tmp_maze = [[1]*size[1] for _ in range(size[0])]
    dx,dy = [(1,2), (-1,-2), (0,0), (0,0)],[(0,0), (0,0), (1,2), (-1,-2)]
    make(1, 1,tmp_maze)
    tmp_maze[1][1],tmp_maze[size[0]-2][size[1]-2] = 2,3
    return tmp_maze

# 画像を作成する
def create_image_from_maze(maze, file_path,pixel_size=20):
    # 色のマッピング
    colors = {
        0: (255, 255, 255),  # 白
        1: (0, 0, 0),        # 黒
        2: (0, 0, 255),      # 青
        3: (0, 255, 0)       # 緑
    }
    width = len(maze[0]) * pixel_size
    height = len(maze) * pixel_size
    image = Image.new("RGB", (width, height), "white")
    pixels = image.load()
    for y in range(len(maze)):
        for x in range(len(maze[0])):
            color = colors.get(maze[y][x], (255, 255, 255))
            for dy in range(pixel_size):
                for dx in range(pixel_size):
                    pixels[x * pixel_size + dx, y * pixel_size + dy] = color
    image.save(file_path)
    return image

実行する際はコレ
# 迷路を作成する
size = (31, 101) # height , width
maze = make_maze(size)

# 画像を生成
image = create_image_from_maze(maze,"image.png")
plt.figure(figsize=(16,10))
plt.imshow(image, cmap='gray')
plt.axis('off')
plt.show()
download

こんな感じで迷路が出来上がります。

黒が壁で、白が通路
青がスタート、緑がゴールです。

いい感じで横長の迷路が
出来ているんじゃないでしょうか

縦は31ピクセル固定として
横は100円分で
31ピクセルとかにすれば
いい感じに再現出来るんじゃないかと。

もっと大きな迷路も
作成は出来ると思うので
サイズ変更して試してみて下さい。

Google Colabなどで
実行できると思います。


今回は迷路の自販機を再現する
迷路作成のコードについてでした。

それでは。

今回はSpotifyの音声エフェクトライブラリ
Pedalboardを試してみました。

解説動画はこちら

 


Pedalboard

spotify pedalboard

Spotifyの開発した
音声用のエフェクトライブラリです。

本来のペダルボードは
ss_pedal

こんな感じのイメージで
ギタリストが良く使う
足踏みエフェクター群を
沢山置いた板(エフェクトセット)
という意味合いかと思います。

それを再現出来るライブラリということになります。

pedalboardの
中にはたくさんのエフェクターが
内包されています。


インストール方法

Google Colabでの使用方法ですが
一般PCも同じ方法でコマンド一発です。
!pip3 install pedalboard


ライブラリのインポート

pedalboardを使用するには
始めにたくさんのライブラリを
読み込んでおく必要があります。
import librosa
from pedalboard.io import AudioFile
from pedalboard import (
    Pedalboard,
    Gain,
    Chorus,
    Phaser,
    Reverb,
    Compressor,
    LadderFilter,
)
import IPython.display as display
from matplotlib import pyplot as plt
librosaは音声用のライブラリです。
matplotlibに関しては後ほど
音声波形の可視化に使用します。



エフェクターの種類

一般的な音声エフェクターと同じ
エフェクトが存在します。

Compressor:音を圧縮する
Gain 音量を上げる
Chorus:コーラスをつける(ちょっと厚みを付ける)
Phaser:位相を変え、うねりを付加する(シュワシュワさせる)
LadderFilter:フィルターをかける
Reverb:反響音をつける



サンプルコード


音声ファイルの読み込み

まずはじめに音声ファイルの読み込みを行います。

librosaのサンプル音声ファイルの「trumpet」
を使っていきます。
(サンプリングレート44.1KHz、モノラル、音の長さ5秒)
# サンプル音源を読み込む
audio, sr = librosa.load(librosa.example('trumpet', hq=True), sr=44100)
print(type(audio))
print(audio.size)

display.Audio(audio, rate=sr)

どんな音声になるかは
動画を見てみて下さいね。



エフェクトをかける

ライブラリで読み込んだ関数
Pedalboard を用いて音声を変換できます。
変換後の音声の変数 = Pedalboard([エフェクト関数 , ・・・])

Pedalboardの引数には
エフェクト関数をリストで渡します。

読み込みしたライブラリ のエフェクト関数が
それに該当します。


コンプレッサー
ゲイン
コーラスをかけるコードはこうなります。
# コンプレッサー、ゲイン、コーラス
board = Pedalboard([
    Compressor(threshold_db=-50, ratio=25),
    Gain(gain_db=30),
    Chorus(),
])
effected_audio = board(audio ,sr)

# エフェクトかけた音源を再生 display.Audio(effected_audio, rate=sr)

エフェクト関数にはそれぞれ
引数が指定できるものも有りますが
ここでは割愛しますので
細かく設定したい方はSpotifyの資料を
みてみて下さい。


オリジナル音声との波形の違い


オリジナルの音声と
エフェクトをかけた後の音声の違いを
可視化するコードはこちらです。
# オリジナル音源
plt.figure()
plt.subplot(2, 1, 1)
librosa.display.waveshow(audio, sr=sr, color='b')
plt.title('Original')

# エフェクトかけた音源
plt.subplot(2, 1, 2)
librosa.display.waveshow(effected_audio, sr=sr, color='r')
plt.title('Effected')

plt.tight_layout()
plt.show()
download

こんな感じで結果が見れます。


音声を保存する


Colabでも、エフェクトをかけた音声を保存できます。
# オーディオファイルの保存
with AudioFile('ファイル名.wav', 'w', sr, num_channels=1) as f:
    f.write(effected_audio)

指定したファイル置き場に音声ファイルが出力されます。



最後に

ギターは弾けませんが
BOSSのエフェクターとか結構好きで
あれを沢山集めるのって
すごく憧れが有るんですよねー

それをデジタル上で再現できてしまう
ライブラリになるので
色々遊んでみて下さい。

それでは。

このページのトップヘ