乙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チートシート


 

今回はR1ファイナリスト
ルシファー吉岡さんのネタを
検証してみることとしました。

解説動画はこちら





はじめに

R1ファイナリスト
ルシファー吉岡大先生のネタに
東京中のデリ全部呼んでみた

というネタがあります。

このネタの中では
大体の店舗数と
平均在籍人数がうたわれていましたが
今、このネタを実践すると
おおよそ幾ら掛かるのか

大雑把なフェルミ推定してみたいと思います



計算方法

店舗数 x 在籍人数 x 平均金額
金額はネタと同じく
60分あたりの金額とします。



1.東京都のデリバリー店舗数

こういうのは警視庁の許可がいりますね。

無店舗型性風俗特殊営業1号 の許可数が
店舗数になると思います。


こんな資料をみつけました

こちらに
令和4年12月末時点の
許可数が出ていました。



2.在籍人数を推測する

全ての店舗のHPが
WEB上に存在する訳では
無さそうだったので

「シティー天国」というサイトがあったので
そこのデータを引っ張ってくる事にしました。


うまく取れなかったのを除いて
在籍数の分布は
ninzuu

こんな感じになります。
これの平均在籍人数を求めました。



3.料金はどうなっているのか

そのお店の紹介に載っている
コースの料金を元に
60分あたりのコース料金を計算しました。


kakaku

分布はこんな感じになります。
この平均値を一人当たりの料金とします。


実際にかかる金額はいくらになるか

1.店舗数 : X件
2.平均在籍人数 : Y人
3.60分平均料金 : Z円

X * Y * Z = ・・・・・

金額は動画を見てみて下さいね。


最後に

いやーこんな素敵なネタを思いついて下さるなんて
素晴らしいですね

発想が神の領域侵犯です。

2024年のR1ファイナリストらしいので
是非優勝していただきたいと思います。

それでは。

最近、やけに地震が多いので
気になって調べて
データを取って可視化してみました。


解説動画はこちら


 
今回のコードは
気象庁の地震データを取得して
そのデータを可視化するコードです。

先にデータが必要になるので
気になった人はコードを実行してみて下さい。

Jupyter Notebook や Google Colab で
実行できると思います。


地震データを取得する



気象庁から地震の観測データを取得して
データフレームにします。

やりたい人は、30分くらいかかるので
気をつけてやって下さい。

リンクを取得
import re
import time
import pandas as pd
import requests
from tqdm import tqdm
from bs4 import BeautifulSoup

domain = "https://www.data.jma.go.jp/"
index_url = "https://www.data.jma.go.jp/svd/eqev/data/daily_map/index.html"
res = requests.get(index_url)
soup = BeautifulSoup(res.content, "html.parser")
eq_link = [i.get("href") for i in soup.find_all("a") if len(i.get("href"))==13]
print(len(eq_link))

リンクからデータを取得する
# 地震データをテキストから取得
def data_pick(text):
    row_data = []
    for row in [i for i in text.split("\n") if len(i)>1][2:]:
        row = row.replace("° ","°")
        for i in range(7,1,-1):
            row = row.replace(" "*i, " ")
        row = row.replace(":"," ")
        tmp = row.split(" ")
        row_data.append(tmp[:-1])
    return row_data

all_data = []
for day in tqdm(eq_link):
    url = "https://www.data.jma.go.jp/svd/eqev/data/daily_map/" + day
    #print(url)
    res = requests.get(url)
    soup = BeautifulSoup(res.content, "html.parser")
    time.sleep(3.971)
    text_data = soup.pre.text
    all_data += data_pick(text_data)
    #break

print(len(all_data))


データを加工してデータフレームにする
columns=["年","月","日","時","分","秒","緯度","経度","深さ(km)","M","震央地名"]
df = pd.DataFrame(all_data,columns=columns)
df = df[df["M"] != "-"].reset_index(drop=True)
df = df.astype({"M": float , "深さ(km)":int, "年":int,"月":int,"日":int})
df["M2"] = df["M"]//1
df["年月日"] = df.apply(lambda x : "{0}{1:02}{2:02}".format(x["年"],x["月"],x["日"]),axis=1)

def lat_lon_10(x):
    tmp = x.split("°")
    degree = int(tmp[0])
    minute = int(tmp[1].split(".")[0])
    second = int(tmp[1].split(".")[1].split("'")[0])
    # 度、分、秒を10進法で表現
    decimal_degree = degree + minute/60 + second/3600
    return decimal_degree

df["緯度10"] = df["緯度"].map(lat_lon_10)
df["経度10"] = df["経度"].map(lat_lon_10)
df["size"] = df["M2"] ** 3

うまく実行できたら
df という変数にデータが格納されると思います。

エラーが出る人は
ライブラリが足りなかったり
通信がうまくいっていなかったり
色々な事象が起きると思いますが
全部は対応出来ませんので
がんばって解決して下さい。


データを見てみる


データを取得できたら
データを見てみましょう。

変数 df に対して
条件を指定すれば
絞り込みをしてみる事ができます。

マグニチュード別の地震回数
df[["M2","震央地名"]].groupby("M2").count().sort_index(ascending=False)
スクリーンショット 2024-03-02 16.29.41

日本地図にプロットする


日本地図にプロットするには
緯度と経度が必要です。

df 変数にはカラムとして
作成しているので
地図に表示する事ができます。

import plotly.express as px

fig = px.scatter_mapbox(
    data_frame=df[df["M2"]>=5],
    lat="緯度10",
    lon="経度10",
    hover_data=["年月日","深さ(km)"],
    color="M2",
    size="size",
    size_max=20,
    opacity=0.3,
    zoom=4,
    height=700,
    width=1500)

fig.update_layout(mapbox_style='open-street-map')
fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0})
fig.show()
output_88_0




千葉県だけにすると
こんな感じです。

output_106_0


色々な条件で絞り込んで
地図に反映させる事ができます。

最近は地震が多いので
何かが起きている可能性がありますね。

警戒を強める意味でも
データを見る価値はあるかもしれません。

今回は地震のデータを取得して
それを可視化するコードについてでした。

それでは。


今回は仮想通貨の自動売買ロジックの
組み立て方のチュートリアルコードを
動かしてみました。


解説動画はこちら



はじめに


今回は書籍
「日給300万円のSS級トレーダーが明かすbotterのリアル」
のチュートリアルコードを動かす方法などについてです。


書籍画像
No description has been provided for this image


チュートリアルのコードはこちら
githubのコード

こちらのコードは
そのままでは動かない可能性もあるので
それ含めて動かせる様にしてみました。


チュートリアル環境の構築


1.Dockerを用いる方
Readme通りにやるだけです。
# コードのクローン
git clone https://github.com/richmanbtc/mlbot_tutorial.git

# Dockerの起動
cd mlbot_tutorial
docker-compose up -d

# Jupyterの起動
# http://localhost:8888 をブラウザで開く

2.自分のJupyter環境 or Colabでやる方
必須ライブラリをインストールしておく必要があります。
# Jupyterの人は全部
ccxt : 仮想通貨取引用のライブラリ
TA-lib : 指標の計算ライブラリ

# Colabの人は不要
numba : Pythonの高速化ライブラリ
scikit-learn : 機械学習用ライブラリ
lightGBM : 機械学習のモデル構築ライブラリ

なおインストール方法は割愛します。
インストールが自力で進める方だけ
進んでください。


チュートリアルの進め方


ここからはチュートリアルのコードを
そのまま進める形になります。

チュートリアルのコードを
そのまま実行してみて下さい。

チュートリアルはこのような
内容になっていました。

1.ライブラリのインポート
2.データ取得
3.データ読み込み
4.前処理
5.目的変数の計算
6.モデルの学習
7.バックテスト


このうち
2.データ取得
3.データ読み込み
に関しては環境によっては
うまく行えない可能性がありました。

ライブラリの差異などにより
うまくデータが生成されませんでした。

なので代わりのコードを
置いておきます。

データ取得のコード
import urllib.request
import os
import time
from datetime import datetime, timedelta
# SSLの問題が有ったら追加
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

def get_date_range(start_date, end_date):
    start = datetime.strptime(start_date, '%Y-%m-%d')
    end = datetime.strptime(end_date, '%Y-%m-%d')
    date_range, current_date= [],start
    while current_date <= end:
        date_range.append((current_date.year, current_date.month, current_date.day))
        current_date += timedelta(days=1)
    return date_range

def get_data(market,start_date,end_date,data_dir = "data"):
    data_dir = f"{data_dir}/{market}"
    url_base = 'https://api.coin.z.com/data/trades/{0}/{1}/{2:02}/{1}{2:02}{3:02}_{0}.csv.gz'
    dates = get_date_range(start_date, end_date)
    for d in dates:
        year,month,day = d
        url = url_base.format(
            market,
            year,
            month,
            day
        )
        file_name = os.path.basename(url)
        if not os.path.exists(f"{data_dir}/{year}"):
            os.makedirs(f"{data_dir}/{year}")
        save_path = os.path.join(f"{data_dir}/{year}", file_name)
        urllib.request.urlretrieve(url, save_path)
        time.sleep(1.37)
# マーケットと開始日、終了日を指定
market = "BTC_JPY"
start_date = "2024-01-01"
end_date = "2024-02-22"
get_data(market,start_date,end_date)

実行するとこのコードが置いてある
ディレクトリ配下にdataが配置されます。

データの読み込み
import os
import glob

def make_df(file_path,interval_sec):
    df = pd.read_csv(file_path)
    df = df.rename(columns={'symbol': 'market',})
    df['price'] = df['price'].astype('float64')
    df['size'] = df['size'].astype('float64')
    df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True)
    df['side'] = np.where(df['side'] == 'BUY', 1, -1).astype('int8')
    df['timestamp'] = df['timestamp'].dt.floor('{}S'.format(interval_sec))
    df.set_index("timestamp", inplace=True)
    volume = df.groupby('timestamp')['size'].sum().rename('volume')
    ohlc = df['price'].resample('1T').ohlc()
    df_merged = ohlc.merge(volume, left_index=True, right_index=True,how="left")
    df_merged = df_merged.fillna(method="ffill")
    df_merged = df_merged.rename(columns={"open":"op","high":"hi","low":"lo","close":"cl"})
    return df_merged
# データの保存場所を指定
data_dir = "data/BTC_JPY/2024/"
interval_sec = 60
df = pd.DataFrame()

for file_path in sorted(glob.glob(data_dir + "*.gz")):
    tmp_df = make_df(file_path,60)
    df = pd.concat([df,tmp_df],axis=0)

こちらのコードを実行すると
変数 df が生成されます。

チュートリアルで用いられている
データを読み込んだ変数になっているので
そのまま使用する事ができると思います。

データが読み込めたかどうかは
次のコードで確認してみて下さい。
df[["cl"]].plot(figsize=(12,4))
plt.show()
download


前処理部分のコードの追加

コードの前処理部分の最初で
手数料を記入するコードがあります。

チュートリアルが作成されてからだいぶ経っており
手数料などが更新されていないため
適宜追加してみて下さい。

maker_fee_history = [
    {
        # https://coin.z.com/jp/news/2020/08/6482/
        # 変更時刻が記載されていないが、定期メンテナンス後と仮定
        'changed_at': '2020/08/05 06:00:00Z',
        'maker_fee': -0.00035
    },
    {
        # https://coin.z.com/jp/news/2020/08/6541/
        'changed_at': '2020/09/09 06:00:00Z',
        'maker_fee': -0.00025
    },
    {
        # https://coin.z.com/jp/news/2020/10/6686/
        'changed_at': '2020/11/04 06:00:00Z',
        'maker_fee': 0.0
    },

    ### 追加
    {
        # 現在値
        'changed_at': '2023/08/05 06:00:00Z',
        'maker_fee': -0.0003
    },
]

あとはチュートリアルを
上から実行していけば
動いていくだろうと思います。

累積リターンのところで
次のような画像が出ればほぼほぼ成功してると思います。

download-1



特徴量の重要度の算出


一般的な機械学習では
予測にどの変数が寄与したのか
重要度を算出することが多いです。

こちらがチュートリアルの
モデルの重要度を算出するコードになります。
feature_importance = model.feature_importances_

# 特徴量名と重要度を紐づける
feature_importance_df = pd.DataFrame({'Feature': features, 'Importance': feature_importance})

# 重要度の降順でソート
feature_importance_df = feature_importance_df.sort_values(by='Importance', ascending=False).reset_index(drop=True)

# 結果の表示(トップ10)
feature_importance_df.head(10)
Feature Importance
0 HT_DCPERIOD 145
1 ADXR 137
2 BBANDS_upperband 125
3 KAMA 124
4 BBANDS_lowerband 116
5 LINEARREG 114
6 MFI 109
7 MACD_macdsignal 107
8 HT_PHASOR_quadrature 106
9 HT_DCPHASE 102


この意味合いとしては
chatGPTによると次の様な意味になっています。



HT_DCPERIOD: Hilbert Transform - Dominant Cycle Period
(ヒルベルト変換 - 優勢サイクル期間)と呼ばれる指標
時系列データの優勢周期を表します。この特徴量は
トレンドのサイクル性を捉えるために使用されます。


ADXR: Average Directional Movement Index Rating
(平均方向運動指数レーティング)は
トレンドの強さを測定する指標

特に、ADX(Average Directional Index)に基づいて計算され
トレンドの強さを示します。


KAMA: Kaufman's Adaptive Moving Average
(カウフマンの適応的移動平均)は移動平均線の一種
価格変動の速さに応じて重みを調整し
より滑らかな移動平均線を提供します。

BBANDS_upperbandおよびBBANDS_lowerband: Bollinger Bands
(ボリンジャーバンド)は、移動平均線を中心に上下に
標準偏差を加えたバンドをプロットする指標

上側バンドと下側バンドは、価格の上下の範囲を示し
価格の過熱やサポート/レジスタンスのレベルを示すのに使用されます。


LINEARREG: Linear Regression(線形回帰)は
価格や指標の値の線形トレンドを推定するための手法です。

この特徴量は、過去のデータからの線形回帰に基づいて
将来のトレンドを予測するために使用されます。


HT_PHASOR_quadratureおよびHT_PHASOR_inphase:
Hilbert Transform - Phasor Components
(ヒルベルト変換 - フェーザーコンポーネント)は
サイン波の位相成分と直交成分を表す指標です。

これらの特徴量はサイクル性や位相関係を捉えるために使用されます。

MFI: Money Flow Index(マネーフローインデックス)は
取引量と価格の関係を用いて買い圧力と売り圧力を計算する指標
主に過買いや過売りの状態を示すのに使用されます。


BETA: ベータは、株式のリスクを示す指標で
市場全体(代表的な指数など)との相関関係を指します。

ベータが1より大きい場合、株価の変動が市場の変動よりも
大きいことを示し、逆にベータが1より小さい場合はその逆を意味します。


ULTOSC: Ultimate Oscillator(アルティメットオシレータ)は
短期、中期、長期の3つの期間を用いてトレンドの強さを計算する指標
主に過買いや過売りの状態を示すのに使用されます。


HT_DCPHASE: Hilbert Transform - Dominant Cycle Phase
(ヒルベルト変換 - 優勢サイクル位相)は
優勢サイクルの位相を表す指標です。この特徴量は
トレンドの位相情報を捉えるために使用されます。


STOCHF_fastk: Stochastic Fast %K
(ストキャスティクスファスト%K)は
価格の変動範囲内での位置を示す指標
主に過買いや過売りの状態を示すのに使用されます。


おまけ

自分もチュートリアルを参考に
別のモデルを構築してみました。

説明変数は同一
目的変数を5分後の価格
に設定し

学習期間は
予測行の前期間step分のみ(step=30)
というモデルにし

1分ずつスライドして
学習しては予測を行うようにしました。

これが予測結果です。

実測と予測値の差分のプロット
download-3

実測と予測値の差分のヒストグラム
download-2

実測と予測のプロット
download-4


学習と予測を近いところで行っているので
ズレすぎることは少ないですね。

最後に予測が現在価格を上まっている条件で
5分後価格と現在価格の差分の累計をプロット
download-5

見事に右肩上がり
予測はかなり機能している様に見えました。


最後に

あくまでシミュレーション上ですが
予測が機能している様に見えるので
売買のロジックに組み込むことも出来るかもしれません。

簡易なロジックとしては
5分後価格予測が現在価格を上まっていたら買い
5分後に売る

5分後価格予測が現在価格を下まっていたら売り
5分後に買う

というような感じですね。

仮の予測モデルは出来ているので
あとは売買ロジック周りを構築すれば
仮想通貨の自動売買botが出来上がります。

その辺りも、もう少し進めていけたらと
思っています。

今回は仮想通貨の自動売買bot本の
チュートリアルを動かしてみたでした。

それでは。

今回は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

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

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

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


このページのトップヘ