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

Python

プログラミング未経験の方のための
プログラミング学習講座を作成しました

その名も
「1時間で学べるPythonプログラミング」


講義動画はこちら




この講座は初学者の方が
短時間でPython言語を学ぶことのできる
プログラミング学習用の講座です

プログラミングが分からないない方は
Python言語を通じて
プログラミングの基礎を学習できます

講座は動画に加えてGoogle Colabを用いて
手元でコードを動かすことのできます
コードがどう動くのかを確認をしながら
進めていってください

資料はここ:
Google Colabの資料


00:00 1.はじめに
02:13 2.導入方法
02:55 3.GoogleColaboratoryの操作方法
06:19 4.Pythonの計算の基礎
27:27 5.Pythonの制御文
42:14 6.Pythonのクラス
49:11 7.Pythonのその他構文
64:30 8.まとめ

なおPythonチートシートを作成しています。

コーディングに迷った際に役に立ち

WEB検索する時間を無くして

作業時間を効率化できます。

note
Pythonチートシート


 

今回はブラック-ショールズ方程式で
オプション価格を計算する方法についてです


解説動画はこちら




ブラック-ショールズ方程式とは

デリバティブの価格づけに現れる
偏微分方程式のことです。

デリバティブは株式や債券、通貨、商品などの
原資産から派生した金融商品の総称のことです。

ブラックショールズ方程式は主に
ヨーロピアン・オプション(満期時のみ行使可) の
理論価格を求めるためのモデルになっています。


オプション取引とは

金融商品のデリバティブの一種で
ある原資産について、あらかじめ決められた
将来の一定の日または期間において
事前に定めた権利行使価格で取引できる
「権利」のことです

原資産を
買う権利についてのオプションを「コールオプション」
売る権利についてのオプションを「プットオプション」
と呼んでいます。



日経225オプション

日経225オプションの価格情報があります。


オプション取引の価格計算ツール(日本取引所)

価格計算ツールもついているので
今回はこの計算方法をPythonで再現します。


ブラックショールズの計算式について

モデルの前提条件

•    価格は幾何ブラウン運動に従う
•    市場は完全で、裁定取引がない
•    無リスク金利は一定
•    ボラティリティ(変動率)は一定
•    配当は考慮しない(配当あり版も考慮は可)


使用する変数

S 現在の価格(Spot Price)
K 権利行使価格(Strike Price)
T 満期までの残存期間(年単位)
r 無リスク金利(年利)
σ 価格のボラティリティ(年率標準偏差)

ブラック-ショールズの数式

コールオプション価格(買う権利):
スクリーンショット 2025-07-12 17.06.54


プットオプション価格(売る権利):
スクリーンショット 2025-07-12 17.06.59

こんな感じの計算式になっていますが
途中、d1,d2というものが必要になります。

d_1, d_2 の定義
スクリーンショット 2025-07-12 17.08.28

なお、N(x) は 標準正規分布の累積分布関数(CDF)になります。


オプション計算のPythonコード

下記の関数で、オプション価格を計算できます。
import numpy as np
from scipy.stats import norm

def black_scholes_option_price(S, K, T, r, sigma, option_type='call'):
    """
    ブラック-ショールズ方程式によるオプション価格の計算
    S: 現在の株価
    K: 権利行使価格
    T: 残存期間(年単位)
    r: 無リスク金利(年利)
    sigma: ボラティリティ(年率標準偏差)
    option_type: 'call' か 'put'
    """
    d1 = (np.log(S / K) + (r + sigma**2 / 2) * T) / (sigma * np.sqrt(T))
    d2 = d1 - sigma * np.sqrt(T)
    if option_type == 'call':
        price = S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
    elif option_type == 'put':
        price = K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)
    else:
        raise ValueError("option_type must be 'call' or 'put'")
    return price

実際に値を入れて計算する場合はこのようになります。
日数で計算したい場合は年単位なのでうまく合わせます。
# パラメータ例
S = 39425       # 現在の価格
K = 40000       # 権利行使価格
T = 62/365      # 満期まで62日
r = 0.0         # 無リスク金利 0%
sigma = 0.1883  # ボラティリティ 18.83%

call_price = black_scholes_option_price(S, K, T, r, sigma, option_type='call')
put_price = black_scholes_option_price(S, K, T, r, sigma, option_type='put')

print(f"Call Option Price: {call_price:.4f}")
print(f"Put Option Price : {put_price:.4f}")
Call Option Price: 963.0367
Put Option Price : 1538.0367




まとめ

この計算結果をどう応用するかですが
オプション取引の戦略を決定するために使います。

市場価格 > 理論価格

オプションが「割高」
プットまたはコールを「売る(ショート)」

市場価格 < 理論価格

オプションが「割安」
プットまたはコールを「買う(ロング)」


ということになります。

この辺りを実際の売買手順フローに落とし込むと
① 必要データ収集(S, K, T, r, σ)
        ↓
② BSモデルで理論価格算出(call/put)
        ↓
③ 実際の市場価格と比較(割高・割安を判断)
        ↓
④ 戦略選定(買う/売る or 戦略組み合わせ)
        ↓
⑤ 証券口座で売買、モニタリング

という感じになるので
自動取引ができるんじゃ無いかと
画策しているところです。

今回はブラックショールズ方程式による
オプション価格の計算方法についてでした。

今回はここまでです
それでは。






今回はドルコスト平均法による
積立投資のシミュレーションです

解説動画はこちら



ドルコスト平均法とは

価格が変動する金融商品に対して
一定金額を定期的に購入していく投資方法のことです

今回は毎月10万円積み立てるとして
それがどうなるかをシミュレーションしていきます。



eMaxisSlim米国株式S&P500

リンク先

S&P500指数(配当込み、円換算ベース)に
連動する運用成果を目指す投資信託
2018年7月3日に設定された商品です。

今回はこのデータを用いて
シミュレーションをしていきます。



eMAXIS Slim 米国株式 S&P500の価格推移

価格のデータを定義して
価格推移をだしてみます。

import numpy as np
import pandas as pd
import plotly.express as px
import seaborn as sns
import matplotlib.pyplot as plt

# データ定義
data = {
    2018 : [0,0,0,0,0,0,10038,10458,10709,11051,10188,10468],
    2019 : [8809,9854,10408,10565,10900,10031,10725,10978,10491,10890,11106,11680],
    2020 : [11873,11892,10825,9474,10655,11205,11465,11886,12707,12182,11767,12989],
    2021 : [13339,13407,14010,15217,15754,15924,16540,16712,17301,16677,18267,18005],
    2022 : [19291,18130,17601,19347,18798,18658,18052,19265,19394,18280,20283,19728],
    2023 : [17690,18714,19167,19388,20234,20666,22867,23260,23411,22911,22677,24155],
    2024 : [24154,25486,27473,28563,28573,29833,31693,29763,29789,29974,31336,32768],
    2025 : [33928,34065,32500,30512,28931,30867]
}

records = []
for year, prices in data.items():
    for month_idx, price in enumerate(prices):
        month = month_idx + 1
        if price == 0:
            continue
        date_str = f"{year}-{month:02d}-01"
        date = pd.to_datetime(date_str)
        records.append({"Date": date, "Price": price})

df = pd.DataFrame(records)
df = df.sort_values("Date")

# Plotlyでプロット
fig = px.line(df, x="Date", y="Price", title="Emaxis Slim value")
fig.show()
スクリーンショット 2025-06-21 16.43.01


良い感じに右肩上がりのようですが
この商品を毎月10万円ずつ積み立てたらどうなっていたでしょうか



2018年から2025年まで毎月積み立てた際のシミュレーション

毎月10万円
購入できる購入数量を算出して
2025年の最後の価格で
どれだけのリターンになっているかを算出します。
monthly_investment = 100000  # 毎月XX万円

# シミュレーション開始
total_units = 0  # 累計購入数量
total_invested = 0  # 累計投資金額
for year in sorted(data.keys()):
    for month_idx, price in enumerate(data[year], 1):
        if price == 0:
            continue
        units_bought = monthly_investment / price
        total_units += units_bought
        total_invested += monthly_investment

latest_year = max(data.keys())
latest_price = data[latest_year][-1]
current_value = total_units * latest_price
profit = current_value - total_invested
roi = (current_value / total_invested - 1) * 100

# 結果表示
print(f"総投資額: {total_invested:,.0f}円")
print(f"最終評価額: {current_value:,.0f}円")
print(f"損益: {profit:,.0f}円")
print(f"累計購入数量: {total_units:.4f}口")
print(f"リターン: {roi:.2f}%")
total_return = (100 + roi) / 100
years = 8
cagr = (total_return ** (1 / years)) - 1
print(f"年平均リターン: {cagr * 100:.2f}%")

総投資額: 8,400,000円
最終評価額: 16,458,895円
損益: 8,058,895円
累計購入数量: 533.2198口
リターン: 95.94%
年平均リターン: 8.77%


この価格推移のデータ上では
年平均8%超

7年で二倍近くの運用成績になっています。



ターンヒートマップ


今度は開始年から終了年までの
リターンヒートマップを出してみましょう。


開始と終了年を設定して
開始終了までのリターンを算出します。

years = list(data.keys())
monthly_invest = 100000
results = pd.DataFrame(index=years[:-1], columns=years[1:])
for start in years[:-1]:
    for end in years[years.index(start)+1:]:
        total_invest = 0
        total_units = 0
        for y in range(start, end+1):
            for price in data[y]:
                if price == 0:
                    continue
                units = monthly_invest / price
                total_units += units
                total_invest += monthly_invest
        # 最後の年の最後の価格で評価額計算
        final_price = [p for p in data[end] if p != 0][-1]
        final_value = total_units * final_price
        return_pct = (final_value - total_invest) / total_invest * 100
        results.loc[start, end] = return_pct

# ヒートマップ描画
results = results.astype(float)
plt.figure(figsize=(10, 7))
sns.heatmap(results, annot=True, fmt=".2f", cmap="coolwarm", center=0)
plt.title("dollar cost averaging method return heatmap (%)")
plt.xlabel("end year")
plt.ylabel("start year")
plt.show()
download

年単位だとどの年で買って売っても
プラスにはなっているようです。



まとめ

eMAXIS Slim 米国株式 S&P500のドルコスト平均法による積み立ては
年単位での運用であればマイナスは無いようです。

ただし、月単位だと乱高下があり
損する場合も有ったので、やはり長期積立で
見守るのが良いのではないかと思われます。

投資は自己判断で行うことが大切です。
その金融商品の良し悪しを判断し
投資するかどうかは人に言われるでなく
自分自身の判断で行うのが重要です。

投資判断を行うための材料作りとして
プログラムを用いたシミュレーションは
かなり役に立ちます。

データさえあればシミュレーションでき
結果の良し悪しから
投資判断の材料に使えるようになると思うので
プログラミングが出来るようになっていると
すぐに試すことができるのでお勧めです。

投資 x プログラミング
というテーマを今後も取り扱っていくので
両方できるようになりたい方は
要チェックしてみてください。

それでは


今回は昔懐かしい
カイジの沼のシミュレーションです。

解説動画はこちら




カイジの沼のシミュレーション

沼とは

漫画『賭博破戒録カイジ』に登場する架空のパチンコ台

帝愛グループの運営するカジノに設置されている
パチンコの中でも特別な台で玉が1発あたり4000円
挑戦は最低300万円からという設定

その一方で、今までの挑戦者が消費した玉が
全てプールされる仕組みになっており
運良く大当たりを引ければ総取りで莫大な額の金を手にできるとか


仕掛け・役物

ここからは確率を計算するための
沼のギミックについてです。

1.釘の森

釘が密集した配置になっている「釘の森」
通常時の設定Cは1/100程度

2.スル

開閉する羽根のある電動役物「スルー」
釘の森を突破した玉を更にふるい落とす
確率は不明なので
ここでは通過率を1/5としておきます

3.3段クルーン

沼の本丸である「三段クルーン」
円形の皿に穴が3つ/4つ/5つ空いていて
当たりの穴に入ると次の段に進める

数学的にはクルーンに入ってさえしまえば
約1/60の確立で当たる


というような設定にはなっていますが
漫画の設定上ではこの台にはイカサマがあり
この確率では入らないようにはなっています。

しかし、今回は考慮しないで
イカサマなしだとどうなるかを見ていきたいと思います。



沼のシミュレーションコード


簡易な確率計算で大当たりしたかどうかだけ返す関数を用いて
シミュレーションしていきます。
最大回数は10万回(4億円相当)とします。

import numpy as np
import random
import pandas as pd
import matplotlib.pyplot as plt

# 確率でヒットするかどうかを判定する
def is_hit(n: int) -> bool:
    return random.randint(1, n) == 1

def numa_charenge():
  # 釘の森の通過
  mori = is_hit(100)
  if not mori:
    return False

  # スルーの通過
  throw = is_hit(5)
  if not throw:
    return False

  # クルーン1段目通過
  first = is_hit(3)
  if not first:
    return False

  # クルーン2段目通過
  second = is_hit(4)
  if not second:
    return False

  # クルーン3段目通過
  third = is_hit(5)
  if not third:
    return False
  else:
    return True


# 1000回施行して測ってみる
ball_price = 4000 # パチンコ玉の価格

def numa_result(num):
  all_result = []
  for a in range(1000):
    cumulative = 0
    for i in range(num):
      cumulative += ball_price
      is_atari = numa_charenge()
      if is_atari:
        all_result.append([i+1, cumulative,1])
        break
      if i == num-1:
        all_result.append([i+1, cumulative,0])
  return all_result

num = 100000 # 1回あたりの最大施行回数
all_result = numa_result(num)

df = pd.DataFrame(all_result,columns=["balls","cumulative","hit"])
df.head()
balls cumulative hit
0 30959 123836000 1
1 29604 118416000 1
...

plt.figure(figsize=(12, 6))
df["cumulative"].hist(bins=20)
plt.show()
download

おおよそこのような結果になります。


どれくらい注ぎ込めば当たるのか?


シミュレーション結果は使った額の累計を入れているので
それを用いれば、その金額以下の回数と
全体の回数から大体の確率を求められます。
times_300 = len(df[df["cumulative"]<3000000])
print(f"300万円で大当たりになる可能性 : {times_300/1000*100} % ")

なおこのシミュレーションの結果は
幾何分布に近似するため以下のようなコードで
回数あたりの大当たり確率の理論値が出せます。
# 幾何分布上の理論値
for i in range(10):
  p = 1 / 30000
  n = (i+1) * 10000
  prob = 1 - (1 - p)**n
  print(f"{i+1:02}万回で当たる確率 : {prob:.6f}")
01万回で当たる確率 : 0.283473
02万回で当たる確率 : 0.486589
03万回で当たる確率 : 0.632127
04万回で当たる確率 : 0.736409
05万回で当たる確率 : 0.811130
06万回で当たる確率 : 0.864669
07万回で当たる確率 : 0.903032
08万回で当たる確率 : 0.930520
09万回で当たる確率 : 0.950215
10万回で当たる確率 : 0.964328


シミュレーション結果もこの理論値に
近似してますね。


まとめ


沼の設定が玉が1発あたり4000円
3万発に1回の確率(100x5x3x4x5)
で当たるのだとしたら3億注ぎ込めば
9割くらいは大当たりするのではないか?

あくまでイカサマが無い事が前提ですが.....
イカサマさえなければ無尽蔵にお金を注ぎ込めるなら
勝てる気がしなくもない今日この頃でした。

それでは

今回はRedisを使ったNoSQL入門です。

解説動画はこちら


 

Redisで超簡単NoSQL入門

今回は Redis のお話です。

Redisは
オープンソースのインメモリデータベース(NoSQL)
になります。


Redisの特徴

キーと値の組み合わせ(キー・バリュー型)でデータを保存
すべてのデータをメモリ上に保持し、高速アクセスを実現
永続化(データの保存)も可能
シングルスレッドながら非常に高速な動作
Python, JavaScript, Go など多くの言語に対応
キャッシュやリアルタイム分析に最適です



Redisの仕組み

データは基本的にRAMに格納(超高速アクセス)
サーバーに配置すれば何処からでもアクセス可能
コマンド操作でデータの入出力などを行う

必要に応じてディスクにバックアップ(永続化)
RDB方式(スナップショット型)
一定間隔でメモリの状態を .rdb ファイルに保存



Redisが扱えるデータ構造

次のようなデータ型でデータを保存できます。

String 文字列、数値
List 順序付きリスト(スタック・キュー)
Set 重複のない集合
Sorted Set(ZSet) スコア付き集合(順序あり)
Hash フィールドと値の連想配列
Stream 時系列データのログ
Geo 地理情報(緯度・経度)



Redisの主なユースケース

Webアプリのキャッシュ
セッション管理
メッセージキュー
トークンや一時的なデータの保存



Colab で Redisを動かす

ここからはGoogle Colabで
Redisを動かしていきましょう。

Google Colabにはredis 関連のものが
インストールされていないので
インストール必要があります。


Redisのインストールと起動
!apt-get update > /dev/null
!apt-get install -y redis-server > /dev/null

Redisの起動
!redis-server --daemonize yes

接続確認
!redis-cli ping

Pythonクライアント(redis-py)のインストール
!pip install redis

Pythonでの接続確認
import redis

# Redisに接続
r = redis.Redis(host='localhost', port=6379, db=0)

# 接続テスト → True が返ればOK
r.ping()


ここからはデータの投入方法と
取得方法です。


String型(基本のキー・バリュー)
値のセット
r.set('キー名', '値')

値の取得
r.get('キー名')
※ データはバイト型で返る

# 値の保存(set)と取得(get)
r.set('message', 'こんにちは!')

print(r.get('message').decode())
こんにちは!




数値操作(インクリメント・デクリメント)

インクリメント
r.incr('キー名')

デクリメント
r.decr('キー名') 

# 値のセット(10)
r.set('count', 10)
print(r.get('count').decode())

# インクリメント(11)
r.incr('count')
print(r.get('count').decode())

# デクリメント(10)
r.decr('count')
print(r.get('count').decode())
10
11
10




List型(スタック/キュー構造)

値をキューに追加
r.rpush('キー名', '値1', '値2', '値3')

値をキューから取得(値を削除しつつ取得)
r.lpop('キー名')

値を全件取得
r.lrange('キー名', 0, -1)

# キューに追加(右) 3つ
r.rpush('task_queue', 'task1', 'task2', 'task3')

# キューから取り出す(左) → task1
print(r.lpop('task_queue').decode())

# 全件取得
print(r.lrange('task_queue', 0, -1))
task1
[b'task2', b'task3']




Hash型(辞書データ)

値のセット
r.hset('キー名', mapping=値)

値の取得(一部)
r.hget('キー名', 'キー名')

値の取得(全部)
r.hgetall('キー名')

# ユーザーデータを登録
r.hset('user:1001', mapping={'name': 'Alice', 'age': '30', 'email': 'alice@example.com'})

# 一部の取得
print(r.hget('user:1001', 'email').decode())

# すべての取得
print(r.hgetall('user:1001'))  # バイナリ形式
alice@example.com
{b'name': b'Alice', b'age': b'30', b'email': b'alice@example.com'}




Set型(重複なし集合)

値のセット
r.sadd('キー名', '値1', '値2', '値3')

値の取得(全部)
r.smembers('キー名')

# 値のセット(python は重複しない)
r.sadd('tags', 'python', 'redis', 'ai', 'python')
print(r.smembers('tags'))
{b'ai', b'redis', b'python'}



Sorted Set(ZSet)でランキング

値のセット
r.zadd('キー名', 値)

値の取得(昇順)
r.zrange('キー名', 開始インデックス, 終了インデックス, withscores=True or False)

値の取得(降順)
r.zrevrange('キー名', 開始インデックス, 終了インデックス, withscores=True or False)

# スコア付きで追加(ランキング)
r.zadd('ranking', {'Alice': 100, 'Bob': 80, 'Charlie': 90, 'Jon':20})

# スコアの降順で表示(上位3名)
print(r.zrevrange('ranking', 0, 2, withscores=True))

# スコアの昇順で表示(下位2名)
print(r.zrange('ranking', 0, 1, withscores=False))
[(b'Alice', 100.0), (b'Charlie', 90.0), (b'Bob', 80.0)]
[b'Jon', b'Bob']



キーの一覧と値の削除

キーの一覧を取得
r.keys('*')

値の削除
r.delete("キー名")

# すべてのキー
print(r.keys('*'))  

# 一括削除
r.delete('message', 'count', 'task_queue', 'user:1001', 'tags', 'ranking')  
print(r.keys('*')) 
[b'tags', b'user:1001', b'ranking', b'message', b'count', b'task_queue']
[]



ユースケースでの使い方

デジタル広告業界
広告クリックの誤クリック判定(5秒の一時記録)
クリックから5秒以内のクリックを無効にする

import redis
import time

# Redisに接続(ローカル or クラウド)
r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)

def track_ckick(user_id: str, ad_id: str, timeout_sec: int = 5):
    key = f"clicked:{user_id}:{ad_id}"
    if r.exists(key):
        return "No (Already Counted)"
    else:
        r.setex(key, timeout_sec, 1)
        return "OK (New Click)"

print(track_ckick("user123", "ad789"))  # => OK
time.sleep(3)
print(track_ckick("user123", "ad789"))  # => NO
time.sleep(3)
print(track_ckick("user123", "ad789"))  # => OK (5秒過ぎた)
OK (New Click)
No (Already Counted)
OK (New Click)



まとめ

Redisが使えるようになると
プログラミングやシステム開発の幅が広がります

様々な業界の多用途にマッチする使い方があり
大規模なキャッシュサーバー構築なども出来るので
システム開発ではNoSQLの使用は必須項目です

まずは入門編で使い方を学んでみましょう
それでは

このページのトップヘ