PythonでFinTech講座 https://python-fin.tech Pythonを学んで投資に活用するブログ Sun, 01 Jun 2025 01:18:34 +0000 ja hourly 1 https://python-fin.tech/wp-content/uploads/2024/07/cropped-0cedf80a00d4ae19601e849c627722c7-32x32.png PythonでFinTech講座 https://python-fin.tech 32 32 【Python】株のセクターの相関係数を可視化する方法を解説 https://python-fin.tech/sector/ Sun, 11 May 2025 12:46:28 +0000 https://python-fin.tech/?p=1754

株式投資では、業種や産業ごとに分類されたセクター(例:金融、IT、エネルギーなど)によって似通った値動きをしたり、逆に異なった値動きをすることがあります。セクター間の相関関係を知り、相関関係の低いセクターの銘柄を選択する ... ]]>

株式投資では、業種や産業ごとに分類されたセクター(例:金融、IT、エネルギーなど)によって似通った値動きをしたり、逆に異なった値動きをすることがあります。
セクター間の相関関係を知り、相関関係の低いセクターの銘柄を選択することでリスクを分散させることができます。

この記事では、Pythonを使って株のセクター間の相関係数を可視化する方法を、初心者でもわかりやすく解説します。

セクターの相関係数とは

セクターの相関関係とは、異なる業種(セクター)同士の株価やリターンがどれくらい同じ方向に動くか(相関性)を示します。
相関係数は「-1」から「+1」までの値をとり、+1に近いほど値動きが似ており、0に近いほど独立、-1に近いほど逆の動きをします。

日本株のセクターは、「東証業種別株価指数」による33業種区分(水産・農林業、鉱業、建設業など)がありますが、本記事ではこれを集約したTOPIX-17(17業種)区分のETFで相関関係を可視化します。

相関係数を可視化する方法

今回は、以下のライブラリを使用します。pipでインストールしてください。

  • yfinance: 価格データを取得するためのライブラリ。
  • seaborn: データ可視化ライブラリ。
  • matplotlib: 2Dや3Dのグラフを描画するためのライブラリ。
  • pandas: 時系列データを処理するためのライブラリ。
Python
pip install yfinance seaborn matplotlib pandas

相関係数の可視化のサンプルコード

Python
import yfinance as yf
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import japanize_matplotlib

# 取得する銘柄リスト
tickers = ["1617.T", "1618.T", "1619.T", "1620.T", "1621.T", "1622.T", "1623.T", "1624.T", "1625.T", "1626.T", "1627.T", "1628.T", "1629.T", "1630.T", "1631.T", "1632.T", "1633.T"]

# 株価データを取得(過去1年分)
data = yf.download(tickers, interval="1d", period='1y')["Close"]

# リターンを計算(対数リターン)
returns = np.log(data / data.shift(1))

# 相関係数を計算
correlation_matrix = returns.corr()

# ヒートマップを描画
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap="coolwarm", linewidths=0.5, fmt=".2f", cbar_kws={'label': '相関係数'})
plt.title("株価リターンの相関関係ヒートマップ")
plt.show()

これを実行すると、以下のヒートマップが出力されます。
このヒートマップから1621(医薬品セクター)が他の銘柄と比較して相関関係が低いことが視覚的にわかります。

ポイントの解説

取得する銘柄リスト

Python
# 取得する銘柄リスト
tickers = ["1617.T", "1618.T", "1619.T", "1620.T", "1621.T", "1622.T", "1623.T", "1624.T", "1625.T", "1626.T", "1627.T", "1628.T", "1629.T", "1630.T", "1631.T", "1632.T", "1633.T"]

TOPIX-17(17業種)で取得するETFのコードとセクターは次の通りです。

  • 1617:食品
  • 1618:エネルギー資源
  • 1619:建設・資材
  • 1620:素材・化学
  • 1621:医薬品
  • 1622:自動車・輸送機
  • 1623:鉄鋼・非鉄
  • 1624:機械
  • 1625: 電機・精密
  • 1626:情報通信・サービスその他
  • 1627:電力・ガス
  • 1628:運輸・物流
  • 1629:商社・卸売
  • 1630:小売
  • 1631:銀行
  • 1632: 金融(除く銀行)
  • 1633:不動産

株価データを取得

Python
# 株価データを取得(過去1年分)
data = yf.download(tickers, interval="1d", period='1y')["Close"]

yfinanceで株価データを取得します。

yfinanceの使い方については過去の記事をご覧ください。

リターンを計算

Python
# リターンを計算(対数リターン)
returns = np.log(data / data.shift(1))

data / data.shift(1)で当日の株価を前日の株価で割って、リターンを計算します。

相関係数を計算

Python
# 相関係数を計算
correlation_matrix = returns.corr()

corr()メソッドで各列の間の相関係数が計算されます。

ヒートマップを描画

Python
# ヒートマップを描画
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap="coolwarm", linewidths=0.5, fmt=".2f", cbar_kws={'label': '相関係数'})
plt.title("株価リターンの相関関係ヒートマップ")
plt.show()

seabornheatmapメソッドでcorrelation_matrixのヒートマップを出力することができます。

まとめ

今回は株のセクターの相関係数を可視化する方法について解説しました。

セクターの相関関係は、業種ごとの値動きの似通い方を示し、景気や市場環境、業種再編などで変化します。

相関係数はセクター以外でも応用できるので、商品CFDなどでも試してみてください。

]]>
【初心者向け】ChatGPTでPythonのエラーを解決する方法|よくあるトラブルと対処法まとめ https://python-fin.tech/error-chatgpt/ Sun, 13 Apr 2025 06:26:12 +0000 https://python-fin.tech/?p=1759

プログラミングをしていると、避けられないのがエラーの発生です。初心者のうちは、エラーの意味が分からず手が止まってしまうことも多いですよね。 そんなときに強い味方になるのが、ChatGPTなどのAIチャットツールです。 C ... ]]>

プログラミングをしていると、避けられないのがエラーの発生です。
初心者のうちは、エラーの意味が分からず手が止まってしまうことも多いですよね。

そんなときに強い味方になるのが、ChatGPTなどのAIチャットツールです。

ChatGPTを使えば、学習効率を大きく上げることができます。

しかし、エラーの原因を正しく伝えなければ的確なアドバイスをもらえないこともあります。

この記事では、ChatGPTを使ってPythonのエラーを効率よく解決する方法を初心者向けにわかりやすく解説します。

ChatGPTはエラーの「相談相手」として優秀!

ChatGPTは、自然な言葉で質問できるAIツールです。コードのバグ修正やエラーメッセージの解釈、エラーの解決方法を提案してくれます。

例えば、以下のようなことができます。

  • エラー文をコピペして原因を聞く
  • コード全体を貼り付けてレビューしてもらう
  • どう書けばいいかわからない時のコードの提案

ただし、うまく使うには正しい伝え方が大切です。

ChatGPTでエラーを相談する時のポイント

まずはChatGPTに「pythonのエラーの対処方法を教えて」と依頼しましょう。Pythonエラー対処方法というスレッドができるので、エラーの対処方法を教えてもらうことができます。

エラーメッセージをそのまま貼り付ける

次にエラーメッセージをそのまま貼り付けてみましょう。ChatGPTが対処方法を提案してくれます。

エラーメッセージをそのまま貼ることで、ChatGPTが正確に状況を把握できます。

コードの該当部分を一緒に貼る

エラーだけでは情報が足りない場合は、コードの前後を一緒に載せるのがポイントです。

例えば、次のように質問をします。

TeX
Pandasを使ってCSVファイルを読み込んでいますが、以下のエラーが出ます。

NameError: name 'df' is not defined

以下が該当コードです。

data = pd.read_csv('data.csv')
df.plot()

このように「エラー内容」「コード」をまとめて伝えると、ChatGPTが正確に判断してくれます。

何をしたかったのか目的を書く

エラーを解決するには「どうなれば成功か」をChatGPTに伝えるのが大事です。

例えば、「CSVから読み込んだデータでグラフを表示したいです」というように具体的に指示をします。

目的が明確だと、ChatGPTはより適切な修正コードを提案してくれます。

よくあるエラーと解決方法

SyntaxError(文法エラー)

SyntaxErrorは、コードの構文や書き方が間違っていることを示すエラーです。Pythonでは、コードが実行される前に構文チェックが行われるため、文法的な問題がある場合は即座にエラーが表示されます。このエラーが発生するとプログラムの実行は停止するので修正が必要です。

エラーの解決方法は、エラーメッセージを確認し、指摘された行や周辺コードを修正します。

IndentationError(インデントのミス)

IndentationErrorは、コードのインデント(文章の行頭に空白)が不適切な場合に発生するエラーです。Pythonではインデントがコードブロックを定義するため、インデントの不一致や欠如があるとプログラムは実行されません。

Pythonでは、4つのスペースを使用することが推奨されています。エラーメッセージには問題箇所が示されるので、その行や周辺コードを確認します。

TypeError(不適切な型のオブジェクト)

TypeErrorは、不適切な型のオブジェクトを操作しようとした際に発生するエラーです。

例えば、 整数と文字列を足し算するような異なるデータ型の間で不適切な演算を行った場合に発生します。

Python
result = "Hello" + 123  # エラー発生

解決方法は、データの型を確認して、異なる型同士の操作が必要な場合は、型変換を行います。

まとめ

ChatGPTはエラー解決の強い味方です。ChatGPTを使えば、エラーに悩んで時間を無駄にすることなく、学習効率を大きく上げることができます。

エラーでつまずいたときは、以下の3点セットでChatGPTに質問しましょう!

  • エラーメッセージ
  • 関連コード(前後も含めて)
  • やりたいこと(目的)

どんどん質問してエラー解決力を高めていきましょう!

]]>
【Pythonでテクニカル分析】ARTを計算して視覚化する方法を解説 https://python-fin.tech/art/ Sun, 16 Mar 2025 14:16:51 +0000 https://python-fin.tech/?p=1711

株式や為替、仮想通貨など、様々な金融市場でトレードを行う際に欠かせないのが、適切な分析ツールです。 これらのトレードにおいて、ボラティリティ(価格変動の大きさ)を把握することはとても重要です。その指標の一つとして広く利用 ... ]]>

株式や為替、仮想通貨など、様々な金融市場でトレードを行う際に欠かせないのが、適切な分析ツールです。

これらのトレードにおいて、ボラティリティ(価格変動の大きさ)を把握することはとても重要です。その指標の一つとして広く利用されているのが、ATR(Average True Range)です。

ATRは、相場の変動幅を数値化し、トレード戦略のリスク管理やエントリータイミングの判断に活用できます。

本記事では、ATRの概要から、Pythonを使った計算方法、グラフによる視覚化までを解説します。

ATRとは

ATR(Average True Range)は、指定した期間の価格の変動幅(ボラティリティ)を測るテクニカル指標です。

トレンドの方向性を示すものではなく、どれくらい価格が変動しているかを把握するための指標として使われます。

1970年代半ばに、数々の革新的なテクニカル指標を開発したJ. Welles Wilder Jr.氏が、著書「New Concepts in Technical Trading Systems」で紹介しました。

ATRの使い方

ATRは主に次の目的で使用されます。

ボラティリティの測定

  • ATRが高い:価格変動が大きい(ボラティリティが高い)
  • ATRが低い:価格変動が小さい(ボラティリティが低い)

損切りの設定

ATRを利用して、ボラティリティに応じた損切りラインを設定する手法があります。

例えば、「エントリー価格からATRの1.5倍を損切りとする」などです。

トレンドの強さを判断

ATRが急上昇すると、新たなトレンドが発生する可能性があります。

逆にATRが低いままだと、レンジ相場(横ばい)が続く可能性があります。

ATRの計算方法

ATRの計算方法は以下の手順で行います。

まず1日の値動きで、以下の3つの値のうち最大のものをTrue Range(TR)とします。

  • 当日の高値 – 当日の安値
  • |当日の高値 – 前日の終値|(絶対値)
  • |当日の安値 – 前日の終値|(絶対値)

次に、TRの値を使ってATRを計算します。一般的には14期間の指数移動平均を使用します。

ATR = (前日のATR * (n – 1) + 今日のTR) / n

ここで、nは期間数(通常14)です。

PythonでATRを計算するコード

ATRを計算しグラフで表示するには、以下のライブラリを使用します。

  • pandas: 時系列データを処理するためのライブラリ。
  • NumPy: 数値計算を効率的に行うためのライブラリ。
  • yfinance: 価格データを取得するためのライブラリ。
  • mplfinance: グラフの描画に使用します。

以下のコードを使って、ATRの計算とグラフの表示をします。使用するデータは、2024年の日経平均株価です。

Python
import pandas as pd
import numpy as np
import yfinance as yf
import mplfinance as mpf

# 期間の設定
period = 14

# 株価データを取得(例:日経平均株価のデータを使用)
ticker = yf.Ticker('^N225')
data = ticker.history(start='2024-01-01', end='2024-12-31')

# TRの計算
high_low = data['High'] - data['Low']
high_close = np.abs(data['High'] - data['Close'].shift(1))
low_close = np.abs(data['Low'] - data['Close'].shift(1))

# TRは3つの値の中で最大のもの
tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)

# ATRの計算
atr = tr.rolling(window=period).mean()

# ATRを追加してプロット
apds = [
    mpf.make_addplot(atr, panel=1, color='red', ylabel='ATR')
]

mpf.plot(
    data,
    type='candle',  # ローソク足チャート
    addplot=apds,
    style='yahoo',
    title='Stock Price with ATR',
    ylabel='Price',
    volume=False
)

上記のコードを実行すると、以下のようにローソク足とARTのグラフが出力されます。

ポイントの解説

Python
# 期間の設定
period = 14

periodでARTの期間を設定します。通常は14期間ですが、変更したい場合はこの数値を変更してください。

Python
# TRの計算
high_low = data['High'] - data['Low']
high_close = np.abs(data['High'] - data['Close'].shift(1))
low_close = np.abs(data['Low'] - data['Close'].shift(1))

TRの計算でのポイントは、.shift(1)で当日と前日の価格が比較できるように計算する値をずらします。そして、Numpyの関数のnp.absで計算した値を絶対値にします。

Python
# TRは3つの値の中で最大のもの
tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)

TRは上記で計算した値の最大値になるので、.max(axis=1)で行ごとの最大値を求めます。

Python
# ATRの計算
atr = tr.rolling(window=period).mean()

最後にTRの移動平均線を計算すれば、ARTを求めることができます。

おわりに

この記事では、Pythonを使ってARTを計算し、グラフで視覚化する方法を解説しました。

ARTは、トレンドの方向性を示すものではなく、どれくらい価格が変動しているかを把握するための指標です。他のテクニカル指標と併せて用いることで、より精度の高いトレードに繋がるでしょう。

Pythonを活用すれば、ARTに限らず様々な指標を簡単に計算できます。この記事をきっかけに、ぜひ他のテクニカル指標にも挑戦してみてください。

]]>
【Python】yfinanceが使えなくなった時の対処方法を紹介 https://python-fin.tech/finance-error/ Thu, 06 Mar 2025 14:38:52 +0000 https://python-fin.tech/?p=1715

先日、yfinanceで株価を取得しようとしたところ、エラーで取得できなくなっていました。 結果的に次の2つをすることで使用できるようになりました。 今回は、yfinanceが使えなくなった時の対処方法を紹介します。 y ... ]]>

先日、yfinanceで株価を取得しようとしたところ、エラーで取得できなくなっていました。

結果的に次の2つをすることで使用できるようになりました。

  • yfinanceのアップデート
  • 株価を取得する記述の変更

今回は、yfinanceが使えなくなった時の対処方法を紹介します。

yfinanceが使えなくなった時の症状

yfinanceを使用した時に、次のようなエラーが発生しました。

TeX
Failed to get ticker '^N225' reason: Expecting value: line 1 column 1 (char 0)
[*********************100%***********************]  1 of 1 completed

1 Failed download:
['^N225']: YFTzMissingError('$%ticker%: possibly delisted; no timezone found')

YFTzMissingErrorというのは、タイムゾーンが見つからないというエラーのようです。

なぜこのようなエラーが発生するようになったのかは、はっきりわかりませんが、株価の参照元に何らかの仕様変更があったのかもしれません。

対処方法

結果的に次の2つをすることで、これまで通りに株価を取得できるようになりました。

yfinanceのアップデート

yfinanceをアップデートすることで、先程のYFTzMissingErrorが発生しなくなりました。記事執筆時点での最新バージョンは、0.2.54です。

Python
pip install -U yfinance

アップデートで株価は取得できるようになりましたが、取得したデータを使用したところ、次のエラーが発生しました。

TeX
ValueError: Data for column "Open" must be ALL float or int.

ValueErrorで、「Open」の列のデータはすべて float 型または int 型である必要があるということです。

デバックすると、float 型で問題ないように思います。

このエラーについて検索すると、株価を取得する記述の変更でエラーが出なくなるという記述がありました。

株価を取得する記述の変更

株価を取得する記述の変更することで、先程のValueErrorが発生しなくなりました。

これまでは、yf.downloadでエラーは発生していませんでしたが、具体的には次のように変更しました。

Python
ticker = yf.Ticker('^N225')
data = ticker.history(start='2024-01-01', end='2024-12-31')

ソース:https://stackoverflow.com/questions/79133206/mplfinance-error-data-for-column-open-must-be-all-float-or-int

なぜこの方法でエラーが発生しなくなるのかは詳しくわかりませんが、これでyfinanceが使用できるようになると思います。

まとめ

以上、yfinanceが使えなくなった時の対処方法を紹介でした。

yfinanceは使い勝手がいいので、この方法で使えるようになって良かったと思います。

過去の記事のソースコードも順次修正していきます。

]]>
カップウィズハンドルとは?Pythonでパターン検索する方法を紹介 https://python-fin.tech/cupwithhandle/ Tue, 04 Feb 2025 12:41:45 +0000 https://python-fin.tech/?p=1652

株式投資やFXなどのチャート分析において、「カップウィズハンドル(Cup with Handle)」は強力なトレンド転換パターンの一つです。特に、上昇トレンドの中で現れる「押し目買い」の絶好のポイントとして、多くのトレー ... ]]>

株式投資やFXなどのチャート分析において、「カップウィズハンドル(Cup with Handle)」は強力なトレンド転換パターンの一つです。
特に、上昇トレンドの中で現れる「押し目買い」の絶好のポイントとして、多くのトレーダーに活用されています。

本記事では、カップウィズハンドルの概要の解説とPythonでのパターン検索する方法について紹介します。

カップウィズハンドルとは

カップウィズハンドルとは、価格チャートが「カップ(カップ型の底)」と「ハンドル(小さな押し目)」の形を形成するチャートパターンです。
このパターンが完成すると、価格が上昇しやすい傾向があります。

アメリカ屈指の投資家であるウィリアム・J・オニール氏の著書「オニールの成長株発掘法」で、最も重要な株価パターンとして紹介されています。
この本は、CAN-SLIM投資法などの投資家にとって有益な情報がたくさん書かれているので一読の価値ありです。

カップウィズハンドルの形状

カップウィズハンドルは、カップを横からカップを横から見たような形状をしています。

カップウィズハンドルの形状には次のような特徴があります。

  • カップ部分:価格がゆるやかに下落し、底を形成した後、再び上昇してカップの右端まで戻る。
  • ハンドル部分:カップの右端付近で小さな調整(押し目)が発生する。
  • ブレイクアウト:ハンドルの高値を超えると強い上昇トレンドが発生することが多い。

カップウィズハンドルの条件

カップウィズハンドルの成立には次のような条件があります。

カップの部分の条件

カップの部分の条件として次のようなものがあります。

  1. カップの部分が形成される前に少なくとも30%以上の上昇トレンドである。
  2. カップの部分の形成期間が3~6ヶ月である。
  3. カップの深さは12〜33%である。
  4. カップの底は丸いU字型がよい。

取っ手の部分の条件

取っ手の部分の条件として次のようなものがあります。

  1. 取っ手の部分の形成期間は1~2周間以上である。
  2. 取っ手はカップの半分より上に形成される。
  3. 取っ手の下落幅は8〜12%以内である。

なぜカップウィズハンドルが形成されるのか

カップウィズハンドルが形成される理由として、個人投資家の心理や機関投資家の動向が関係していると言われています。

まず、株価が上昇トレンドで30%以上の値上がりをした後に値下がりすると、利益の確定売で投資家は株を手放していきます。

株価が12〜33%値下がりしたところで機関投資家が株を買い集めます。
3~6ヶ月かけて買い集めることで、握力の弱い投資家はふるい落とされ、カップの底は丸いU字となります。

機関投資家の買い集めが完了して、価格が上昇に転じ始めると、個人投資家も乗り遅れまいと参入をして価格の上昇が加速します。

しかし、直近の高値に近づくと、利益確定をする投資家が増えて価格は下落に転じます。
ここでカップウィズハンドルの取手の部分が形成されます。

株は機関投資家が買い集めているので、8〜12%下落すると利益確定をする投資家がいなくなり、再び上昇に転じます。

そして、直近の高値を更新すると、上昇トレンドが発生してると判断され、更に価格は上昇していきます。

よって、取っ手の高値を更新するタイミングが買いポイントになるのです。

Pythonでカップウィズハンドルのパターン検索する方法

続いてPythonでカップウィズハンドルのパターン検索する方法を紹介します。

カップウィズハンドルのパターン検索は、相関関数をもとにカップウィズハンドルを探します。

相関係数とは、2つのデータの間にある関係の強弱を測る指標です。
-1から1までの値を取り、相関係数が1に近いほど正の相関、-1に近いほど負の相関、0に近いほど相関がないことを意味します。

つまり、カップウィズハンドルの形状と株価の日足の形状を比較して、1に近いものを抽出します。

証券コードのリストの用意

まずは、証券コードのリストを用意します。

おすすめの方法は、SBI証券の銘柄スクリーニングでCSVをダウンロードする方法です。

今回は日経225の銘柄からチャート形状検索をしたいと思うので、採用指数の日経225にチェックをします。

そして、CSVダウンロードをクリックするとscreener_result.csvというファイル名のデータが手に入ります。

ダウンロードしたscreener_result.csvをPythonの作業ディレクトリに移動させておきます。

ライブラリのインストール

今回は、以下のライブラリを使用します。pipでインストールしておいてください。

  • numpy: 数値計算を効率的に行うためのライブラリ。
  • pandas: 時系列データを処理するためのライブラリ。
  • yfinance: 価格データを取得するためのライブラリ。
  • mplfinance: グラフの描画に使用します。
Bash
pip install numpy pandas yfinance mplfinance

チャート形状銘柄検索をするサンプルコード

Python
import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt

# 期間の設定 '3mo','6mo'から選択
period = '6mo'


# 株価を取得する関数
def get_data():
    df = pd.DataFrame(columns=['code'])
    csv_df = pd.read_csv('screener_result.csv', header=None, skiprows=1, usecols=[0, 1])
    df['code'] = csv_df[0]
    ticker_symbol_dr = []

    for i in range(len(df)):
        ticker_symbol_dr.append((str(df.iloc[i, 0])) + ".T")

    symbol_data = yf.download(ticker_symbol_dr, period=period, threads=False)
    df = pd.DataFrame(symbol_data['Close'])
    df = df.dropna(axis=1)

    return df


data = get_data()
term = len(data)

# 株価データの正規化
normalization_df = (data - data.min(axis=0)) / (data.max(axis=0) - data.min(axis=0))

# X軸の正規化
x = list(map(lambda v: v / float(term - 1), range(0, term)))

# 4次多項式でカップウィズハンドルの形状を再現
cup_with_handle = list(map(lambda v: (-39.50813 * (v / float(term - 1)) ** 4) + (86.90238 * (v / float(term - 1)) ** 3) -
                                     (59.7321 * (v / float(term - 1)) ** 2) + (13.13785 * (v / float(term - 1))), range(0, term)))

# 4次多項式を描写
plt.plot(x, cup_with_handle)
plt.show()

# 相関関数を計算してカップウィズハンドルのパターンを検索
for i in range(len(normalization_df.columns)):
    a = normalization_df.iloc[:, i].tolist()
    corr = np.corrcoef(a, cup_with_handle)[1, 0]

    if corr > 0.5:
        print(f"{normalization_df.columns[i]} 相関係数:{corr}")

これを実行すると、4次多項式で再現したカップウィズハンドルの形状のグラフと、相関関係が強い銘柄が出力されます。

上記のグラフの0の形状と日足の形状を比較します。

記事を執筆時の相関関係が高い銘柄は、次の3銘柄でした。

4507
7269
9434

おおむねカップウィズハンドルに似た形状を抽出できたと思います。

ポイントの解説

株価データの取得

Python
# 期間の設定 '3mo','6mo'から選択
period = '6mo'


# 株価を取得する関数
def get_data():
    df = pd.DataFrame(columns=['code'])
    csv_df = pd.read_csv('screener_result.csv', header=None, skiprows=1, usecols=[0, 1])
    df['code'] = csv_df[0]
    ticker_symbol_dr = []

    for i in range(len(df)):
        ticker_symbol_dr.append((str(df.iloc[i, 0])) + ".T")

    symbol_data = yf.download(ticker_symbol_dr, period=period)
    df = pd.DataFrame(symbol_data['Close'])
    df = df.dropna(axis=1)

    return df

まず、株価を取得する期間を設定します、3ヶ月、6ヶ月から選択します。

csv_df = pd.read_csv('screener_result.csv', header=None, skiprows=1, usecols=[0, 1])でSBI証券からダウンロードしたscreener_result.csvを読み込みます。

yfinanceで各銘柄の株価を取得する場合は、末尾に".T"が必要なので、ticker_symbol_dr.append((str(df.iloc[i, 0])) + ".T")".T"を付け加えながらticker_symbol_drに加えています。

symbol_data = yf.download(ticker_symbol_dr, period=period, threads=False)で株価を取得して、df = pd.DataFrame(symbol_data['Close'])で終値のみを使用するようにしています。

また、欠損値がある場合はdf = df.dropna(axis=1)で除外します。

正規化

Python
# 株価データの正規化
normalization_df = (data - data.min(axis=0)) / (data.max(axis=0) - data.min(axis=0))

# X軸の正規化
x = list(map(lambda v: v / float(term - 1), range(0, term)))

正規化とは、データを一定の規則に基づいて変形し、比較や分析を容易にする処理のことです。

今回の場合はMin-Max正規化という手法で、各銘柄の最小値が0、最大値が1になるように株価の値を正規化します。

また、ラムダ式を用いて、二次関数で使用するX軸の正規化もします。

4次多項式でカップウィズハンドルの形状を再現

Python
# 4次多項式でカップウィズハンドルの形状を再現
cup_with_handle = list(map(lambda v: (-39.50813 * (v / float(term - 1)) ** 4) + (86.90238 * (v / float(term - 1)) ** 3) -
                                     (59.7321 * (v / float(term - 1)) ** 2) + (13.13785 * (v / float(term - 1))), range(0, term)))

Desmosというサイトのグラフ計算機を用いて4次多項式でカップウィズハンドルの形状を再現します。
5箇所のオレンジ色の点を調整して、下記のようなグラフを作成しました。

このグラフの4次多項式は次のようになります。

この値をPythonで計算することで、カップウィズハンドルの形状を再現しました。

相関関数を計算してカップウィズハンドルのパターンを検索

Python
# 相関関数を計算してカップウィズハンドルのパターンを検索
for i in range(len(normalization_df.columns)):
    a = normalization_df.iloc[:, i].tolist()
    corr = np.corrcoef(a, cup_with_handle)[1, 0]

    if corr > 0.5:
        print(f"{normalization_df.columns[i]} 相関係数:{corr}")
        

NumPyの相関係数を求めるnp.corrcoef関数で相関係数を求めます。

corr = np.corrcoef(a, pattern)[1, 0]で株価の終値と4次多項式との相関係数を求めて、0.5以上の銘柄を出力します。

おわりに

以上、カップウィズハンドルの概要の解説とPythonでのパターン検索する方法について紹介しました。

カップウィズハンドルは、強い上昇トレンドのシグナルとなる重要なチャートパターンです。
大量にある銘柄からこのパターンを見つけるのは大変な作業ですが、Pythonを使うことで数分で候補の銘柄を検索することができます。

投資銘柄の選定に、ぜひ活用してみてください。

]]>
【初心者向け】Pythonのおすすめ学習方法3選を目的別に紹介! https://python-fin.tech/python_study/ Sat, 25 Jan 2025 14:59:04 +0000 https://python-fin.tech/?p=1580

「Pythonでプログラミングができるようになりたい。」 「Pythonの勉強方法がわからない。」 こういった悩みをお持ちではないでしょうか。 Pythonはシンプルなプログラミング言語であり、ポイントを押さえれば初心者 ... ]]>

「Pythonでプログラミングができるようになりたい。」

「Pythonの勉強方法がわからない。」

こういった悩みをお持ちではないでしょうか。

Pythonはシンプルなプログラミング言語であり、ポイントを押さえれば初心者でも比較的簡単に独学で習得できます。

私の場合は、書籍でプログラミングの学習をして挫折した経験があるので、学習サービスを利用して学習する方法をおすすめします。

この記事では、学習の目的を次の3つに分けて学習方法を紹介します。

  • 費用を抑えて学習→Udemy(ユーデミー)
  • 効率的に学習→PyQ(パイキュー)
  • フリーランスや副業を視野に入れて学習→デイトラ

ぜひPythonの学習の参考にしてみてください。

費用を抑えて学習する方法

費用を抑えて学習する方法は、オンライン学習プラットフォームの「Udemy」で学習する方法です。

多彩な講座から自分に合った講座を探そう!

Udemyは、22万件以上も講座のあるオンライン学習のプラットフォームです。講座は買い切りの動画となっており、自分のタイミングとペースで学習ができます。また、わからないことは講師に質問することができます。

普段は定価で販売されていますが、セールのタイミングでは1,500円から購入できるのでコスパ抜群です。また、30日以内の返金保証があるため、満足できない場合は返金可能です。

おすすめ講座は、現役シリコンバレーエンジニアの酒井潤さんが講師の「現役シリコンバレーエンジニアが教えるPython 3 入門 + 応用 +アメリカのシリコンバレー流コードスタイル」です。UdemyのPython講座の中でも人気の講座で評価も高く、Pythonの基礎から応用まで網羅的に解説されています。網羅的な分、コースの総時間は約29時間と長時間ですが、if文やfor文といった基礎編は約8時間なので、まずは基礎編を勉強して、必要性がでてきてから応用編も勉強すればいいと思います。

一方、Udemyで学習するデメリットは、Pythonの実行環境を自分で用意する必要があるところです。講座内で解説されていますが、初心者にとってはハードルが高いかもしれません。またUdemyでの学習はどうしても独学スタイルになるので、モチベーションを保つことが難しいです。自ら進んで学習ができる、自己管理ができる人におすすめです。

\30日間の返金保証!/

Udemyの公式サイトはこちら

効率的に学習する方法

効率的に学習する方法は、オンラインPython学習サービス「PyQ™(パイキュー)」で学習する方法です。

「PyQ™」は、プログラミング初心者にも優しく、また実務的なプログラミングを段階的に学べることを目指し、開発されたPythonに特化したオンライン学習サービスです。ブラウザからPythonの学習ができるので、プログラムをはじめて学びたい未経験者におすすめです。

「PyQ™」では、「教材を見る」「コードを書く」「実際に動かす」のサイクルを繰り返し、実力を高めていきます。24個のコースがあり、数個の問題からなる「クエスト」という単位で学習を進めます。 600クエスト・1500問以上が用意されており、ゲーム感覚で学習を進められるのでモチベーションを維持しやすいです。

以下は用意されているコースの一例です。

  • 7days Python チャレンジ
  • Pythonプログラミングをはじめよう
  • Python文法基礎を学ぼう
  • Python文法を実務レベルに深めよう
  • 実務で役立つPython
  • はじめてのデータベース
  • はじめてのWebアプリケーション
  • データ分析
  • 機械学習
  • 統計分析

「PyQ™」の料金プランは次のようになっています。

個人ライトプラン個人スタンダードプラン
月額料金(税込)3,040円8,130円
プログラミングの基本
Python入門〜中級
Webアプリ開発
データ分析
機械学習
統計入門
アルゴリズム
PyQに投稿された過去質問の閲覧
プロによる質問への回答

有料プランを契約すると、契約期間中はすべてのクエストを学習することができます。

ライトプランとスタンダードプランの違いは、学習サポートの有無です。スタンダードプランでは、現役エンジニアによるQ&A形式の「PyQ学習サポート」を利用できます。学習で困った時にプロに相談したい場合は、スタンダードプランを選択しましょう。

また「PyQ™」では、「7days Python Challenge 」というものがあり、無料でPythonのプログラミングを体験することができます。「7days Python Challenge 」では、7日間でプログラミングの初歩とPython基礎文法を学習することができます。はじめてのプログラミングを手助けするメールサポートもあるので、まずは無料で始めてみましょう。

\まずは無料でPythonの体験!/

フリーランスや副業を視野に入れて学習する方法

フリーランスや副業を視野に入れて学習する方法は、オンラインスクールのデイトラで学習する方法です。

デイトラは年間受講者数8,000人超の国内最大規模のオンラインプログラミングスクールです。完全オンライン形式なので自由なタイミングで学習ができます。Web制作、Webアプリ開発、Webデザインなどの多彩なコースを提供しており、Pythonも学ぶことができます。

Pythonコースは、日常的にPythonをビジネスに使っているプロが作成しています。プログラミング未経験者を想定したカリキュラムなので、ゼロからの学習スタートさせる場合でも安心です。株価予測アプリやYouTube分析アプリなど、実務で活用できるアプリケーション開発スキルを習得できます。

デイトラでは、Pythonについてわからないことや疑問があれば質問ができるサポートが1年間利用できるので、途中で挫折すること無くスキルを習得できます。また、定期的にアップデートされる教材が閲覧期間無制限で利用できるので、マイペースに学習が進められます。

Pythonコースの価格は、プログラミングスクール業界最安級の料金設定(89,800円)で提供されています。口コミで広告費を抑えられているので、他のスクールと比べて安価でコストパフォーマンスが高いです。

料金(税込)89,800円
メンター質問期間1年
教材ボリューム約90日分
教材閲覧期間無期限(アップデート有)

デイトラは未経験者でも挫折せずに、実践的なスキルを効率的に習得できるオンラインスクールとして評価されています。フリーランスや副業を視野に入れている方は検討してみてください。

\体験講座は無料で受講可能!/

デイトラの公式HPを確認する

まとめ

以上、Pythonのおすすめ学習方法3選について紹介しました。

特徴をまとめると、次の表のようになります。

UdemyPyQデイトラ
料金1,500円~3,040円89,800円
学習スタイル動画視聴クエスト形式動画やテキスト
サポート講師に質問できるスタンダードプランであり1年間のサポートあり
公式サイトUdemyPyQデイトラ

それぞれ一長一短があるので、まずはUdemyやPyQから始めてみるといいと思います。

プログラミングのスキルを活かして転職や副業に挑戦したい場合は、デイトラがおすすめです。

まずはPythonのプログラミングスキルを身に付けて、当ブログのテーマである株やFXの自動売買に挑戦してみてください。

]]>
【Pythonでテクニカル分析】ボリンジャーバンドを計算して視覚化する方法を解説 https://python-fin.tech/bolinger-band/ Tue, 07 Jan 2025 21:42:19 +0000 https://python-fin.tech/?p=1561

株式や為替、仮想通貨など、様々な金融市場でトレードを行う際に欠かせないのが、適切な分析ツールです。トレード分析において、ボリンジャーバンドは非常に人気のあるインジケーターの一つです。 トレンドの変化を視覚的に捉え、売買の ... ]]>

株式や為替、仮想通貨など、様々な金融市場でトレードを行う際に欠かせないのが、適切な分析ツールです。トレード分析において、ボリンジャーバンドは非常に人気のあるインジケーターの一つです。

トレンドの変化を視覚的に捉え、売買のタイミングを判断するために役立つので、初心者からベテランまで幅広く活用されています。

この記事では、Pythonを使ってボリンジャーバンドを計算し、視覚的に理解しやすいグラフで表示する方法を解説します。

データを分析してトレンドを見極めたい方や、自分でトレードの戦略を構築したい方にとって、基本的なテクニカル分析手法として押さえておきたい内容です。

ボリンジャーバンドとは

ボリンジャーバンドとは、米国の投資家ジョン・ボリンジャーが開発したトレンド系インジケーターです。移動平均線を中心にその上下に価格の変動範囲(バンド)を描きます。

価格の過熱感や反転の可能性を判断するのに役立ち、相場のトレンドや価格のばらつきを視覚的に確認できる優れたツールです。

まず、ボリンジャーバンドの基本的な構成要素について理解しておきましょう。ボリンジャーバンドは、以下の3つの線(バンド)で構成されます。

  • 中央の線(移動平均線):通常、20期間の単純移動平均線(SMA)を使います。
  • 上部バンド(+2σライン):中央の移動平均線に「標準偏差」を2倍加えたライン。
  • 下部バンド(-2σライン):中央の移動平均線から「標準偏差」を2倍引いたライン。

ボリンジャーバンドの使い方

バンドの幅は価格のボラティリティ(変動率)を反映します。幅が広がるとボラティリティが高く、狭まると低いことを示します。統計学的に、価格が±2σのバンド内に収まる確率は約95.4%です。

ボリンジャーバンドの形状によって、トレンドの変化や継続を予測することができます。主に4つの特徴的な形状があり、それぞれが異なる市場状況を示唆します。

  • スクイーズ:バンド幅が収束した状態で、ボラティリティが低く、もみ合い相場を示します。エクスパンションの前兆とも言えます。
  • エクスパンション:スクイーズからバンド幅が拡大していく状態で、ボラティリティが高まりトレンドが発生する可能性を示しますン。
  • ボージ:エクスパンション後、バンド幅が最大に拡大した状態で、トレンド反転の可能性が高まります。
  • バンドウォーク:トレンドが継続している状態で、上昇トレンドでは価格が+2σに沿って、下降トレンドでは-2σに沿って推移します。この状態が続く限り、トレンドの続伸が期待できます。

ボリンジャーバンドは一般的に逆張りのシグナルとして使用されますが、開発者であるジョン・ボリンジャーは順張りを推奨しています。

スクイーズからエクスパンションを狙う順張りトレード

スクイーズ(バンド幅が狭い状態)からエクスパンション(バンド幅が広がる状態)への移行を確認します。価格が±2σのラインを超えたら、その方向にエントリーします。

そこからバンドウォークが発生すると、より強いトレンドが発生していると判断できるので、ポジションを保有し続けることで利益の拡大に期待が持てます。

ポジション決済は、トレンド転換のサインであるボージが確認できたら行います。

スクイーズを利用した逆張りトレード

ボリンジャーバンドがスクイーズ状態になっているときは、レンジ相場であると言えます。

ボリンジャーバンドでは±2σバンド内に価格が収まる確率は統計学的に約95.4%と高いので、±2σバンド付近で価格が反発する可能性が高くなります。価格が±2σバンドにタッチしたときに逆張りでエントリーをします。

ポジション決済は、中央の移動平均線にタッチするか、反対の2σバンドタッチで行います。

ボリンジャーバンドの計算方法

ボリンジャーバンドの計算式は以下の通りです。一定期間(一般的には20期間の価格データ)をnとします。

  1. ミドルバンド(単純移動平均):
    \begin{eqnarray} SMA  =n SMA  \end{eqnarray}
  2. 標準偏差
    \begin{eqnarray} σ = \sqrt{\frac{1}{n-1} \sum_{i=1}^{n} (x_i – \mu)^2} \end{eqnarray}
    ここで、n はデータ数、xiは各データの終値、μはデータの平均値です。
  3. ±1σ
    単純移動平均±標準偏差
  4. ±2σ
     単純移動平均±標準偏差×2
  5. ±3σ
     単純移動平均±標準偏差×3

Pythonでボリンジャーバンドを計算するコード

ボリンジャーバンドを計算しグラフで表示するには、以下のライブラリを使用します。

  • pandas: 時系列データを処理するためのライブラリ。
  • yfinance: 価格データを取得するためのライブラリ。
  • mplfinance: グラフの描画に使用します。

以下のコードを使って、ボリンジャーバンドの計算とグラフの表示をします。使用するデータは、2024年の日経平均株価です。

Python
import pandas as pd
import yfinance as yf
import mplfinance as mpf

# 期間の設定
period = 20

# 株価データを取得(例:日経平均株価のデータを使用)
ticker = '^N225'
data = yf.download(ticker, start='2024-01-01', end='2024-12-31')

# ボリンジャーバンドの計算
sma = data['Close'].rolling(period).mean()  # 移動平均
std = data['Close'].rolling(period).std()  # 標準偏差
upper_band = sma + (std * 2)  # 上部バンド
lower_band = sma - (std * 2)  # 下部バンド

# ボリンジャーバンドを追加してプロット
apds = [
    mpf.make_addplot(sma, color='blue'),
    mpf.make_addplot(upper_band, color='orange'),
    mpf.make_addplot(lower_band, color='orange')
]

mpf.plot(
    data,
    type='candle',  # ローソク足チャート
    addplot=apds,
    style='yahoo',
    title='Stock Price with Bollinger Bands',
    ylabel='Price',
    volume=False
)

上記のコードを実行すると、以下のようにローソク足とボリンジャーバンドのグラフが出力されます。

ポイントの解説

Python
# 期間の設定
period = 20

periodでボリンジャーバンドの期間を設定します。通常は20期間ですが、変更したい場合はこの数値を変更してください。

Python
sma = data['Close'].rolling(period).mean()  # 移動平均
std = data['Close'].rolling(period).std()  # 標準偏差
upper_band = sma + (std * 2)  # 上部バンド
lower_band = sma - (std * 2)  # 下部バンド

ボリンジャーバンドの移動平均や標準偏差は、pandasの窓関数を使うと簡単に計算できます。

pandasで窓関数を適用するにはrolling()を使います。

移動平均を計算する場合は、sma = data['Close'].rolling(period).mean()のように「データ.rolling(期間).mean()」のように記述します。mean()で指定した期間の平均値を計算できます。

標準偏差も同様に、std()と記述することで指定した期間の標準偏差を計算できます。

あとは、移動平均と標準偏差を使用して±2σを計算します。

おわりに

この記事では、Pythonを使ってボリンジャーバンドを計算し、グラフで視覚化する方法を解説しました。

ボリンジャーバンドは、トレンドの勢いや変化を見る上で有効な指標です。更に、単独ではなく他のテクニカル指標と併せて用いることで、より精度の高いトレードに繋がるでしょう。

Pythonを活用すれば、ボリンジャーバンドに限らず様々な指標を簡単に計算できます。この記事をきっかけに、ぜひ他のテクニカル指標にも挑戦してみてください。

]]>
バルサラの破産確率をPythonで計算しよう! https://python-fin.tech/balsaras_bankruptcy_probability/ Thu, 02 Jan 2025 12:46:45 +0000 https://python-fin.tech/?p=1026

FXなどのトレードにおけるリスク管理は、資金を守るための重要な要素です。 特に、資金が尽きるリスクである「破産確率」を理解することは、トレーダーにとって避けて通れない課題です。 この記事では、「バルサラの破産確率」の計算 ... ]]>

FXなどのトレードにおけるリスク管理は、資金を守るための重要な要素です。

特に、資金が尽きるリスクである「破産確率」を理解することは、トレーダーにとって避けて通れない課題です。

この記事では、「バルサラの破産確率」の計算方法をPythonで実装する方法を解説します。

勝率、リスクリワード比率、リスク許容率といったトレードのパラメータをもとに、自分のトレード戦略の破産確率を計算してみましょう。プログラムを使うことで、戦略のリスクを視覚的に理解し、適切なリスク管理に役立てることができます。

バルサラの破産確率とは?

バルサラの破産確率とは、フランスの数学者ナウザー・バルサラが提唱した理論で、トレーダーの取引手法が破産するリスクを計算する手法です。特に勝率とリスクリワード比率(リスクに対してリターンがどれだけ見込めるか)をもとに、どれほどのリスクをとれば破産しないかを評価します。次の計算式を用いて、バルサラの破産確率を計算していきます。

\begin{eqnarray} x=px^\left( 1+k\right)+(1-p)-x \end{eqnarray}

ここで、

p:勝率(取引で勝つ確率)

k:リスクリワード比率(1回の取引でリスクをどれだけ取って、どれだけの利益を得られるかを示す比率)

x:0 < x < 1を満たす値

となります。

そして、xを用いて破産確率を計算します。

バルサラの破産確率\(=x^\left( n/b\right)\)

ここで、

n: 取引資金

b: リスク許容率(1回の取引で使用する資金の割合)

となります。

バルサラの破産確率を計算するための準備

Pythonで破産確率を計算するには、以下のライブラリを使用します。

  • pandas: データの表形式での処理や表示に使用します。
  • seaborn: ヒートマップの作成に使用します。
  • matplotlib: グラフの描画に使用します。

Pythonで破産確率を計算するコード

以下がPythonでバルサラの破産確率を計算するサンプルコードです。

Python
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt


# 中間方程式の計算
def intermediate_equation(x, p, k):

    return p * x ** (k + 1) + (1 - p) - x


# 中間方程式の解を求める
def solve_equation(win_rate, risk_reward_ratio):
    x = 0
    
    while 0 < intermediate_equation(x, win_rate, risk_reward_ratio) <= 1:
        x += 0.001

    if x >= 1:
        x = 1

    return x


# バルサラの破産確率の計算
def calc_risk_of_ruin(win_rate, risk_reward_ratio, risk_per_trade):
    x = solve_equation(win_rate, risk_reward_ratio)

    return x ** (1 / risk_per_trade)


# 勝率とリスクリワード比率のリストを設定
win_rates = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]  # 勝率のリスト
risk_reward_ratios = [0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5]  # リスクリワード比率のリスト
risk_per_trade = 0.05  # 1トレードあたりのリスク割合(5%)

# ヒートマップ用のデータを保存するためのリスト
heatmap_data = []

# 各パラメータの組み合わせに対して破産確率を計算
for risk_reward_ratio in risk_reward_ratios:
    row = []
    for win_rate in win_rates:
        risk_of_ruin = calc_risk_of_ruin(win_rate, risk_reward_ratio, risk_per_trade)
        row.append(risk_of_ruin * 100)  # パーセンテージに変換
    heatmap_data.append(row)

# pandasのデータフレームに変換
df_heatmap = pd.DataFrame(heatmap_data, index=[f'{rr}' for rr in risk_reward_ratios],
                          columns=[f'{wr * 100:.0f}%' for wr in win_rates])

# ヒートマップを作成
plt.figure(figsize=(10, 6))
sns.heatmap(df_heatmap, annot=True, fmt=".2f", cmap="YlOrRd", cbar_kws={'label': 'Risk of Ruin (%)'})
plt.title("Risk of Ruin Heatmap")
plt.xlabel("Win Rate")
plt.ylabel("Risk-Reward Ratio")
plt.show()

このコードを実行すると、次のようなヒートマップが表示されます。

ヒートマップの赤色のところが破産確率が100%の範囲です。一般的に、破産確率が1%未満の場合を安全圏とみなします。

risk_per_tradeの値を変更することで、リスク許容率ごとのヒートマップを出力することができます。

ポイントの解説

Python
# 中間方程式の計算
def intermediate_equation(x, p, k):

    return p * x ** (k + 1) + (1 - p) - x


# 中間方程式の解を求める
def solve_equation(win_rate, risk_reward_ratio):
    x = 0
    
    while 0 < intermediate_equation(x, win_rate, risk_reward_ratio) <= 1:
        x += 0.001

    if x >= 1:
        x = 1

    return x


# バルサラの破産確率の計算
def calc_risk_of_ruin(win_rate, risk_reward_ratio, risk_per_trade):
    x = solve_equation(win_rate, risk_reward_ratio)

    return x ** (1 / risk_per_trade)

def calc_risk_of_ruin(win_rate, risk_reward_ratio, risk_per_trade):の関数でバルサラの破産確率を計算します。

まず、x = solve_equation(win_rate, risk_reward_ratio)で中間方程式を求めます。

その中身は、xを0から1まで0.001ずつ変化させて、上記のp * x ** (k + 1) + (1 - p) - xを計算します。

xが求まれば、x ** (1 / risk_per_trade)でバルサラの破産確率を計算します。

おわりに

この記事では、バルサラの破産確率をPythonで計算し、ヒートマップで視覚化する方法を解説しました。

破産確率を計算することで、トレード戦略のリスクを客観的に評価し、より安全な資金管理を行うことが可能になります。

しかし、この確率は数学的に計算された表に過ぎず、実際のトレード結果を完全に予測するものではありません。トレードを行う際は、常にリスク管理を意識し、自分の取引スタイルに合わせて破産確率を計算してみてください。

]]>
Pythonで株のキャッシュフローを8つのパターンに分類しよう! https://python-fin.tech/cash_flow/ Sat, 28 Dec 2024 03:31:55 +0000 https://python-fin.tech/?p=1502

株式投資において、キャッシュフローの分析は企業の財務健全性や成長可能性を評価するうえで欠かせないポイントです。キャッシュフローの分析は難しそうに思えるかもしれませんが、Pythonを使えば効率的にデータを整理し、視覚的に ... ]]>

株式投資において、キャッシュフローの分析は企業の財務健全性や成長可能性を評価するうえで欠かせないポイントです。キャッシュフローの分析は難しそうに思えるかもしれませんが、Pythonを使えば効率的にデータを整理し、視覚的に理解できるようになります。

この記事では、企業のキャッシュフローの8つのパターンの特徴とPythonを使って効率的に分析する方法を解説します。

キャッシュフローとは

キャッシュフローとは、一定期間における現金や現金同等物の流れを指します。つまり、企業が実際に受け取ったり支払ったりする現金の動きを表しています。

キャッシュフローは企業の実際の資金繰りを示すため、経営の健全性を判断する重要な指標となります。利益が出ていても、現金の流れが悪ければ企業は倒産する可能性があります。

投資家にとっては、キャッシュフローは企業の将来性や投資価値を判断する上で欠かせない情報です。

企業のキャッシュフローは、主に以下の3つに分類されます。

  1. 営業活動によるキャッシュフロー(CFO)
    • 本業の営業活動で生み出されたキャッシュ。プラスであれば利益をしっかりと生み出している状態です。
  2. 投資活動によるキャッシュフロー(CFI)
    • 設備投資や事業拡大のための支出や、資産売却で得たキャッシュの流れを示します。
  3. 財務活動によるキャッシュフロー(CFF)
    • 借入金や株式発行で調達した資金、あるいは配当金や返済で出ていった資金を指します。

これらのキャッシュフローの組み合わせによって、企業の資金使途や財務方針を8つのパターンに分類できます。

キャッシュフローの8つのパターン

キャッシュフロー計算書の3つの区分(営業活動、投資活動、財務活動)のプラスとマイナスの組み合わせによって、企業の経営状況を8つのパターンに分類できます。

パターン営業CF投資CF財務CF
健全型
改善型
積極型
安定型
救済型
リストラ型
勝負型
大幅見直し型

健全型

本業がうまくいっており、投資を行いつつ、借入金の返済もできている理想的な状態です。

改善型

本業ではあまりキャッシュを稼げていないので、資産売却で得た資金を借入金等の返済に回している可能性があります。不採算事業からの撤退や財務健全化を図っている企業だと見ることができます。

積極型

本業が好調で、借入金などを活用して積極的に投資を行っている状態です。成長のために資金を惜しまない企業と判断されることがあります。

安定型

本業で十分な利益を上げ、資産売却も行い、資金調達も実施している状態です。将来の大型投資に向けて資金を蓄えている企業に見られます。

救済型

本業の赤字を資産売却や借入金で賄っている状態です。銀行の融資姿勢次第では資金繰りに窮する可能性があるため、要注意です。

リストラ型

本業が赤字で、資産売却によって借入金を返済している状態です。この状態が続くと金融機関からの融資が止まる可能性があるため、早急な本業の回復が必要です。

勝負型

本業が赤字であるにもかかわらず、借入金によって設備投資を行っている状態です。事業再建中の企業に多く見られます。

大幅見直し型

本業が赤字にも関わらず、設備投資を行い借入金返済も行っているため、多額のキャッシュが流出している状態です。過去の実績はあるが現在は低迷している企業に多いです。

Pythonを使ったキャッシュフロー分析

次に、Pythonを使って実際にキャッシュフローを取得し、分類するコードを解説します。

必要なライブラリのインストール

pipでyfinanceをインストールします。

Bash
pip install yfinance

yfinanceの詳しい使い方については、下記の記事で解説しているので参考にしてみてください。

【Python】yfinanceの使い方を徹底解説!日本株や米国株の株価データや財務情報、為替データを取得する方法

キャッシュフローを分析するコード

以下は、Pythonを使ってキャッシュフローを8つのパターンに分類するサンプルコードです。

例として、トヨタ自動車のキャッシュフローを分析します。

Python
import pandas as pd
import yfinance as yf

# 企業の証券コードを設定(末尾に.Tを付ける)
ticker = '7203.T'

# 財務データを取得
ticker_info = yf.Ticker(ticker)
cash_flow = ticker_info.cash_flow

# 各データを抽出
cfo = cash_flow.loc['Operating Cash Flow'].fillna(0)
cfi = cash_flow.loc['Investing Cash Flow'].fillna(0)
cff = cash_flow.loc['Financing Cash Flow'].fillna(0)

# データフレームにまとめる
df = pd.DataFrame({
    '営業CF': cfo,
    '投資CF': cfi,
    '財務CF': cff
}).T

df.columns = [col.year for col in df.columns]


# キャッシュフローの分類
def classify_cashflow(cfo, cfi, cff):

    if cfo > 0 and cfi < 0 and cff < 0:
        return "健全型"

    elif cfo > 0 and cfi > 0 and cff < 0:
        return "改善型"

    elif cfo > 0 and cfi < 0 and cff > 0:
        return "積極型"

    elif cfo > 0 and cfi > 0 and cff > 0:
        return "安定型"

    elif cfo < 0 and cfi > 0 and cff > 0:
        return "救済型"

    elif cfo < 0 and cfi > 0 and cff < 0:
        return "リストラ型"

    elif cfo < 0 and cfi < 0 and cff > 0:
        return "勝負型"

    elif cfo < 0 and cfi < 0 and cff < 0:
        return "大幅見直し型"

    else:
        return "未分類"


# 各年の分類結果
for year in df.columns:

    cfo, cfi, cff = df[year]
    category = classify_cashflow(cfo, cfi, cff)
    print(f"{year}年: {category}")

コードを実行すると、以下のような結果が出力されます。

2020年はデータが無かったので未分類となっています。それ以外は、積極型と健全型という結果になりました。

ポイントの解説

財務データを取得

Python
# 財務データを取得
ticker_info = yf.Ticker(ticker)
cash_flow = ticker_info.cash_flow

# 各データを抽出
cfo = cash_flow.loc['Operating Cash Flow'].fillna(0)
cfi = cash_flow.loc['Investing Cash Flow'].fillna(0)
cff = cash_flow.loc['Financing Cash Flow'].fillna(0)

ticker_info = yf.Ticker(ticker)で指定した証券コードの財務データを取得します。ticker_infoの中には様々なデータが含まれているので、cash_flow = ticker_info.cash_flowでキャッシュフローのデータのみを抽出するしています。更に、営業キャッシュフローなどのデータを振り分けています。

キャッシュフローの分類

Python
# キャッシュフローの分類
def classify_cashflow(cfo, cfi, cff):

    if cfo > 0 and cfi < 0 and cff < 0:
        return "健全型"

    elif cfo > 0 and cfi > 0 and cff < 0:
        return "改善型"

    elif cfo > 0 and cfi < 0 and cff > 0:
        return "積極型"

    elif cfo > 0 and cfi > 0 and cff > 0:
        return "安定型"

    elif cfo < 0 and cfi > 0 and cff > 0:
        return "救済型"

    elif cfo < 0 and cfi > 0 and cff < 0:
        return "リストラ型"

    elif cfo < 0 and cfi < 0 and cff > 0:
        return "勝負型"

    elif cfo < 0 and cfi < 0 and cff < 0:
        return "大幅見直し型"

    else:
        return "未分類"

上記の表の条件に合うようにキャッシュフローの分類を行います。キャッシュフローのデータがない場合は、未分類となるようにしています。

おわりに

今回は、株のキャッシュフローの分析方法について解説しました。

キャッシュフローを8つのパターンに分けることで、企業の成長性や財務状況を把握することができます。特に、健全型や安定型が投資対象として良いとされています。

Pythonでキャッシュフローの分析をして、投資判断の参考にしてみてください。

]]>
【Python】GMOコインでビットコイン等の暗号資産の自動売買を始める方法(実践編) https://python-fin.tech/gmocryptocurrency-practice/ Sun, 08 Dec 2024 04:46:43 +0000 https://python-fin.tech/?p=1424

GMOコインでビットコイン等の暗号資産の自動売買を始める方法の準備編では、GMOコインの口座開設方法とAPIの使用方法について解説しました。準備がまだ整っていない方は、下記のリンクから口座の開設から進めてください。 今回 ... ]]>

GMOコインでビットコイン等の暗号資産の自動売買を始める方法の準備編では、GMOコインの口座開設方法とAPIの使用方法について解説しました。準備がまだ整っていない方は、下記のリンクから口座の開設から進めてください。

今回は実践編ということで、基本的な移動平均線のゴールデンクロスとデッドクロスで自動売買をするプログラムを具体的に紹介します。

\登録は無料!/

プログラムの流れ

今回作成するプログラムは、下記のような流れになっています。

  1. 価格情報を取得
  2. ゴールデンクロス・デッドクロスの検出
  3. 売買シグナルに基づいて注文

やっていることは非常にシンプルで、状態を監視して、シグナルが出たら注文を出します。これを繰り返します。

サンプルコード

移動平均線のゴールデンクロスとデッドクロスで自動売買するプログラムは以下のようになります。

Python
import time
from datetime import datetime
from datetime import timedelta
import requests
import hmac
import hashlib
import json
import pandas as pd


apiKey = 'YOUR_API_KEY'
secretKey = 'YOUR_SECRET'

# 設定項目
symbol = 'BTC_JPY'  # 使用する通貨ペア
interval = '1hour'  # 使用する時間軸
order_size = '0.01'  # 注文数量
MA_short_period = 5  # 移動平均線の短期の日数
MA_long_period = 25  # 移動平均線の長期の日数
get_price_period = 10  # 四本値を取得する日数

flag = {
    'order': {
        'side': '',
    },
    'position': {
        'exist': False,
        'side': None,
        'lot': 0.0,
    },
}


# 成行注文を出す関数
def market_order():
    global flag

    while True:
        try:
            timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
            method = 'POST'
            endPoint = 'https://api.coin.z.com/private'
            path = '/v1/order'
            reqBody = {
                'symbol': symbol,
                'side': flag['order']['side'],
                'size': order_size,
                'executionType': 'MARKET'
            }

            text = timestamp + method + path + json.dumps(reqBody)
            sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()

            headers = {
                'API-KEY': apiKey,
                'API-TIMESTAMP': timestamp,
                'API-SIGN': sign
            }

            res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody))
            print(json.dumps(res.json(), indent=2))

            flag['order']['side'] = ''

            return

        except Exception as e:
            print(f'action=market_order error={e}')
            print('10秒後にリトライします')
            time.sleep(10)


# 決済注文を出す関数
def close_order():
    global flag

    while True:
        try:
            timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
            method = 'POST'
            endPoint = 'https://api.coin.z.com/private'
            path = '/v1/closeBulkOrder'
            reqBody = {
                'symbol': symbol,
                'side': flag['order']['side'],
                'size': order_size,
                'executionType': 'MARKET'
            }

            text = timestamp + method + path + json.dumps(reqBody)
            sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()

            headers = {
                'API-KEY': apiKey,
                'API-TIMESTAMP': timestamp,
                'API-SIGN': sign
            }

            res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody))
            print(json.dumps(res.json(), indent=2))

            return

        except Exception as e:
            print(f'action=market_order error={e}')
            print('10秒後にリトライします')
            time.sleep(10)


# ポジションを確認する関数
def check_positions():
    global flag

    timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
    method = 'GET'
    endPoint = 'https://api.coin.z.com/private'
    path = '/v1/positionSummary'

    text = timestamp + method + path
    sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()
    parameters = {
        'symbol': symbol
    }

    headers = {
        'API-KEY': apiKey,
        'API-TIMESTAMP': timestamp,
        'API-SIGN': sign
    }

    res = requests.get(endPoint + path, headers=headers, params=parameters)
    positions = eval(json.dumps(res.json(), indent=2))
    if not positions['data']['list']:
        flag['position']['lot'] = 0
        flag['position']['side'] = None
        flag['position']['exist'] = False

        print('現在ポジションは存在しません')

        return

    flag['position']['lot'] = float(positions['data']['list'][0]['sumPositionQuantity'])
    flag['position']['side'] = positions['data']['list'][0]['side']
    flag['position']['exist'] = True

    print('ポジション数量:' + str(flag['position']['lot']))
    print('売買区分:' + str(flag['position']['side']))

    return


# 四本値を取得する関数
def get_price_data():
    while True:
        try:
            price_data = []
            response_data = []

            if 0 <= datetime.today().hour <= 5:
                today = datetime.today().date()

            else:
                today = datetime.today().date() + timedelta(days=1)

            for i in range(get_price_period):
                period = i - get_price_period
                date = (today + timedelta(days=period)).strftime('%Y%m%d')
                endPoint = 'https://api.coin.z.com/public'
                path = '/v1/klines?symbol=' + symbol + '&interval=' + interval + '&date=' + date

                response = requests.get(endPoint + path)
                response_data.append(response.json())

            for j in range(len(response_data)):
                for i in response_data[j]['data']:
                    price_data.append({'openTime': i['openTime'],
                                       'openTime_dt': datetime.fromtimestamp(int(i['openTime'][:-3]))
                                      .strftime('%Y/%m/%d %H:%M'),
                                       'open': i['open'],
                                       'high': i['high'],
                                       'low': i['low'],
                                       'close': i['close']})

            print('TIME: ' + str(price_data[-1]['openTime_dt']) + ' H: ' + str(price_data[-1]['high']) + ' L: ' +
                  str(price_data[-1]['low']) + ' C: ' + str(price_data[-1]['close']))

            df_price_data = pd.DataFrame(price_data)

            return df_price_data

        except Exception as e:
            print(f'action=get_price_data error={e}')
            print('10秒後にリトライします')
            time.sleep(10)

# 移動平均線のGC、DCを判定してエントリー注文を出す関数
def entry_signal(df):
    global flag

    df['MA-short'] = df['open'].rolling(MA_short_period).mean()
    df['MA-long'] = df['open'].rolling(MA_long_period).mean()

    if df.iat[-2, 6] < df.iat[-5, 7] and df.iat[-1, 6] > df.iat[-4, 7]:
        flag['order']['side'] = 'BUY'
        print('ゴールデンクロスが発生しました。')

    elif df.iat[-2, 6] > df.iat[-2, 7] and df.iat[-1, 6] < df.iat[-1, 7]:
        flag['order']['side'] = 'SELL'
        print('デッドクロスが発生しました。')

    else:
        flag['order']['side'] = ''

    return flag


# メイン処理
check_positions()

while True:

    price = get_price_data()
    entry_signal(price)

    if flag['position']['exist'] is False:
        if flag['order']['side'] == 'BUY':
            print('買いの成行注文を出します。')
            market_order()
            time.sleep(10)

            check_positions()
            time.sleep(60)

        if flag['order']['side'] == 'SELL':
            print('売りの成行注文を出します。')
            market_order()
            time.sleep(10)

            check_positions()
            time.sleep(60)

        elif flag['position']['exist'] is True:
        if flag['position']['side'] == 'BUY' and flag['order']['side'] == 'SELL':
            print('決済をして、売りの成行注文を出します。')
            close_order()
            time.sleep(10)

            market_order()
            time.sleep(10)
            check_positions()
            
            time.sleep(60)

        elif flag['position']['side'] == 'SELL' and flag['order']['side'] == 'BUY':
            print('決済をして、買いの成行注文を出します。')
            close_order()
            time.sleep(10)

            market_order()
            time.sleep(10)
            check_positions()
            
            time.sleep(60)

    time.sleep(60)

ポイントの解説

上記のサンプルコードのポイントについて解説します。

ライブラリ

Python
import time
from datetime import datetime
from datetime import timedelta
import requests
import hmac
import hashlib
import json
import pandas as pd

このプログラムでは、上記のようなライブラリを使用します。インストールされていないライブラリが場合は、pipでインストールしておいてください。

datetimeは現在時刻を取得するためのライブラリです。timestampを作成する際に使用します。

requestsは、APIリクエストを送るためのライブラリです。

hmachashlibは、APIシークレットを暗号化して送るために使うライブラリです。

jsonは、指値価格や注文量などのパラーメータをJSON形式に変換するためにのライブラリです。JSONとは「JavaScript Object Notation」の略で、軽量なテキストベースのデータ交換フォーマットです。

pandasは、データ分析ツールです。取得した価格データの格納や移動平均線を計算するときに使用します。

設定項目

Python
apiKey = 'YOUR_API_KEY'
secretKey = 'YOUR_SECRET'

# 設定項目
symbol = 'BTC_JPY'  # 使用する通貨ペア
interval = '1hour'  # 使用する時間軸
order_size = '0.01'  # 注文数量
MA_short_period = 5  # 移動平均線の短期の日数
MA_long_period = 25  # 移動平均線の長期の日数
get_price_period = 10  # 四本値を取得する日数

flag = {
    'order': {
        'side': '',
    },
    'position': {
        'exist': False,
        'side': None,
        'lot': 0.0,
    },
}

apiKeysecretKeyは、準備編で取得したapiKeysecretKeyを記入してください。

設定項目は、通貨ペアや使用する時間軸などを設定してください。今回はビットコインと円のレバレッジ取引を行います。使用する時間足は1時間で、1回の注文数量は0.01BTCです。

今回は移動平均線のゴールデンクロスとデッドクロスをシグナルにしているので、MA_short_periodMA_long_periodについては、5と25を設定しています。

これらの設定は自由に変更してみてください。詳細については、GMOコインのAPIのドキュメント(https://api.coin.z.com/docs/#)で確認できます。

flagは、注文のシグナルやポジションの状態といった、プログラムが動作しているときの状態を記録しておくものです。

成行注文を出す関数

Python
timestamp = '{0}000'.format(int(time.mktime(datetime.now().timetuple())))
            method = 'POST'
            endPoint = 'https://api.coin.z.com/private'
            path = '/v1/order'
            reqBody = {
                'symbol': symbol,
                'side': flag['order']['side'],
                'size': order_size,
                'executionType': 'MARKET'
            }

            text = timestamp + method + path + json.dumps(reqBody)
            sign = hmac.new(bytes(secretKey.encode('ascii')), bytes(text.encode('ascii')), hashlib.sha256).hexdigest()

            headers = {
                'API-KEY': apiKey,
                'API-TIMESTAMP': timestamp,
                'API-SIGN': sign
            }
            
            res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody))
            

上記のコードは注文を出す定型文です。

こちらで変更するところはreqBodyの内容です。'executionType''MARKET'は成行注文を意味しています。指値注文の場合は'LIMIT'となり、注文価格などを設定する必要があります。詳細については、GMOコインのAPIのドキュメント(https://api.coin.z.com/docs/#)で確認してください。

四本値を取得する関数

Python
def get_price_data():
    while True:
        try:
            price_data = []
            response_data = []

            if 0 <= datetime.today().hour <= 5:
                today = datetime.today().date()

            else:
                today = datetime.today().date() + timedelta(days=1)

            for i in range(get_price_period):
                period = i - get_price_period
                date = (today + timedelta(days=period)).strftime('%Y%m%d')
                endPoint = 'https://api.coin.z.com/public'
                path = '/v1/klines?symbol=' + symbol + '&interval=' + interval + '&date=' + date

                response = requests.get(endPoint + path)
                response_data.append(response.json())

            for j in range(len(response_data)):
                for i in response_data[j]['data']:
                    price_data.append({'openTime': i['openTime'],
                                       'openTime_dt': datetime.fromtimestamp(int(i['openTime'][:-3]))
                                      .strftime('%Y/%m/%d %H:%M'),
                                       'open': i['open'],
                                       'high': i['high'],
                                       'low': i['low'],
                                       'close': i['close']})

            print('TIME: ' + str(price_data[-1]['openTime_dt']) + ' H: ' + str(price_data[-1]['high']) + ' L: ' +
                  str(price_data[-1]['low']) + ' C: ' + str(price_data[-1]['close']))

            df_price_data = pd.DataFrame(price_data)

            return df_price_data

        except Exception as e:
            print(f'action=get_price_data error={e}')
            print('10秒後にリトライします')
            time.sleep(10)

GMOコインの仕様で、0時から5時の間は最新の1時間足が取得できなかったので、if 0 <= datetime.today().hour <= 5:で取得する条件を変えています。

また、GMOコインのPublic APIでは1日単位でしか取得できなかったので、for i in range(get_price_period):で必要な日数を繰り返し取得して、ひとつにまとめています。そしてデータを扱いやすいようにpandas の DataFrameに変換しています。

移動平均線のGC、DCを判定してエントリー注文を出す関数

Python
# 移動平均線のGC、DCを判定してエントリー注文を出す関数
def entry_signal(df):
    global flag

    df['MA-short'] = df['open'].rolling(MA_short_period).mean()
    df['MA-long'] = df['open'].rolling(MA_long_period).mean()

    if df.iat[-2, 6] < df.iat[-2, 7] and df.iat[-1, 6] > df.iat[-1, 7]:
        flag['order']['side'] = 'BUY'
        print('ゴールデンクロスが発生しました。')

    elif df.iat[-2, 6] > df.iat[-2, 7] and df.iat[-1, 6] < df.iat[-1, 7]:
        flag['order']['side'] = 'SELL'
        print('デッドクロスが発生しました。')

    else:
        flag['order']['side'] = ''

    return flag

移動平均線は、Pandas の関数である rollingで簡単に計算することができます。

ゴールデンクロスやデッドクロスの判定方法は、短期と長期の価格の比較が入れ替わったタイミングがゴールデンクロス又はデッドクロスが発生したタイミングとなります。

また、TA-Libというライブラリを使用すれば、ボリンジャーバンドやMACDといったインジケーターを簡単に計算することができます。他のインジケーターも試してみたい場合は、下記の記事も参考にしてください。

【Python】テクニカル分析ライブラリTA-Libの使い方を解説

メイン処理

Python
# メイン処理
check_positions()

while True:

    price = get_price_data()
    entry_signal(price)

    if flag['position']['exist'] is False:
        if flag['order']['side'] == 'BUY':
            print('買いの成行注文を出します。')
            market_order()
            time.sleep(10)

            check_positions()
            time.sleep(60)

        if flag['order']['side'] == 'SELL':
            print('売りの成行注文を出します。')
            market_order()
            time.sleep(10)

            check_positions()
            time.sleep(60)

        elif flag['position']['exist'] is True:
        if flag['position']['side'] == 'BUY' and flag['order']['side'] == 'SELL':
            print('決済をして、売りの成行注文を出します。')
            close_order()
            time.sleep(10)

            market_order()
            time.sleep(10)
            check_positions()
            
            time.sleep(60)

        elif flag['position']['side'] == 'SELL' and flag['order']['side'] == 'BUY':
            print('決済をして、買いの成行注文を出します。')
            close_order()
            time.sleep(10)

            market_order()
            time.sleep(10)
            check_positions()
            
            time.sleep(60)

    time.sleep(60)

まず初めに現在のポジションを確認します。

ポジションがない場合は、entry_signal(price)でゴールデンクロス(デッドクロス)の発生を監視します。シグナルが発生したら、market_order()で注文を実行します。

次にポジションがある場合は、買いポジションのときにデッドクロスのシグナルが発生すると、close_order()で決済注文を出して、market_order()で売り注文を実行します。

おわりに

以上で、暗号資産の自動売買を始める方法についての解説はおわりです。

暗号資産の自動売買のプログラミングのイメージはつかめたでしょうか?

今回は移動平均線のゴールデンクロスとデッドクロスを売買シグナルにしましたが、このまま稼働させても勝てるわけではありません。設定を変更したり、その他のインジケーターを組み合わせて勝てる手法を探してみてください。

実際にプログラミングをすることでスキルも身につけることができるので、サンプルコードを参考にして暗号資産の自動売買をチャレンジしてみてください。

GMOコイン

GMOコイン

GMOインターネットグループの暗号資産FX・売買サービス

  • 新たに「外国為替FX」のAPIサービスを開始
  • 口座開設申し込みから最短10分で取引が可能
  • 現物取引、レバレッジ取引ともにサービスが充実している
  • 取引ツールが高性能かつ使いやすい
]]>