コミュニティ

成功する投資:トレーディングのサイエンス

はじめに

早速ですが、皆さんは投資をしているでしょうか。しているとすれば、どのような投資をしていらっしゃるでしょうか。

世の中には様々な投資対象が存在し、またその投資手法も様々です。投資に関する情報は世の中に溢れています。氾濫していると言ったほうがよいかもしれません。書籍を例に取ると、甘い文句で投資を奨励するライトな入門書から金融の専門書までずらりと並びます。またブログやSNSも重要な情報源となっており、最近では投資向けのYouTubeも人気を集めているようです。

しかしこれだけ多様な情報ソースが存在するにも関わらず、投資で成功を収めることができるのはごく一握りです。少し古いリサーチになりますが2015年の野村證券の個人投資家リサーチでは、通算で利益が出ている個人投資家の割合は9.3%とのことです。どうしてこのような事態に陥ってしまうのでしょうか。投資の初級者の方はどのようなアプローチをしているのか、いくつか例を挙げてみましょう。

  • 流行りのテーマ株の高騰に飛びついて高値掴みをしてしまう
  • バフェットの真似をして四季報から割安成長株を選んでみる
  • 聞きかじった知識を元にPERやROEで銘柄をスクリーニングしてみる
  • 配当狙いで銘柄を保有してみる
  • 雑誌のアナリストが推していた銘柄を購入してみる
  • 企業決算や経済指標の発表に合わせて取引してみる
  • 流行りのESG企業を買ってみる
  • テクニカルを使ってチャートから上昇するか下落するか予想してみる

このような手法を鵜呑みにトレードしてしまい、勤労時間の昼休みが来るたびに含み損を見て落胆している方もいるのではないでしょうか。もう一度言いますが、なぜこのような事態に陥ってしまうのでしょうか。それは、そもそも初級者の方はどのようなトレーディングスタイルが本質的に優位であるか知らないからです

本記事の目的

本記事では兎にも角にも、初級者の方が持つ投資観に対して全く新しい気付きを与えることを念頭に置きました。トレーディングは科学です。科学的なトレーディングとは、すなわち計量的・実証的なトレーディングのことです。

本記事では統計的な考えに基づき、どのようなトレーディングスタイルが優位であるか示します。記事中では、初級者の方が平易に読むことができるようになるべく分かりやすい用語を選び、難しい数式や専門的な考察は可能な限り省略するようにしました。

また、科学的なトレーディングを実行に移すための具体的な手法について手ほどきします。計量的・実証的なトレーディングを行うためにはプログラミングは必須です。本記事ではPythonという言語を用いて必要最低限の検証を行うためのプログラムを記しました。プログラミングと聞いて身構える人も多いでしょうが、この機会に勉強を始めてみるのもよいでしょう。何事も目的意識をもって取り組むことが上達への近道となります。

筆者の投資パフォーマンス

記事のはじめに、まず筆者のこれまでの投資のパフォーマンスを紹介しておきます。いきなり自分が儲けたカネの話をしていやらしいと思われるでしょうが、結果を出していない人が何を言っても説得力はないのです。なお一言加えておきますが、投資において最も重要なことは結果ではなくプロセス管理です。なぜなら後述するように、投資のパフォーマンスは運に左右される部分が大きく、結果のみに基づいて判断すると間違った結論を招いてしまう場合が多いからです。

筆者はもともと本職のエンジニアの傍らで投資をしていましたが、2014年から本格的に資産運用を開始しました。このときのスタートアップ資金は5000万円でした。2016年に現在主力となっている運用システムを開発しました。この運用システムは運用開始から現在までにおよそ1億4000万円を稼ぎ出している我々の旗艦システムです。運用は日本株のTOPIX500と呼ばれる大型銘柄が対象で、直近4年間の平均利回りは40%程度となっています。
02.png

問題提起

では冒頭に上げた初級者のアプローチではなぜ成功することができないのか、何が問題であるのか解説します。ここでいう成功とは、少なくとも5年以上の投資期間において途中で挫折することなく継続的・安定的に所望の利回りを得ることを指します。

まず冒頭の手法によって利益が出るのか出ないのか考えてみましょう。一例として割安でクオリティが高いと考えられる銘柄への投資を考えてみます。株の個別銘柄を選定するとき、証券会社のツールを使って銘柄をスクリーニングする方が多いでしょう。ここではPERが10倍以下でROEが10%以上の銘柄に1ヶ月間投資したときのパフォーマンスを見てみましょう。

集計期間は2017年4月から2020年10月であり、スクリーニングした銘柄を月初に購入して(正確には前月の終値で購入して)、当月の月末の終値で決済した場合の獲得利益の分布図(ヒストグラム)です。このとき、個別銘柄のリターンからマーケットのリターンを差し引いています。こうしなければ日経平均の急騰などの影響を受けてしまい、その獲得した利益が銘柄選定のスキルに依るものなのか、それとも運よくマーケット上昇の恩恵を受けただけなのか、切り分けできないからです。
value_hist3.png

さて、この投資手法で利益は出るのでしょうか。

結論として、利益が出る場合もあれば出ない場合もあります。あなたの投資した銘柄は、上図の分布のうち、最も右端(つまり利益が出た銘柄)かもしれません。はたまた、分布の最も左端(つまり損失が出た銘柄)かもしれません。上図の分布に含まれている銘柄は全てあなたの基準にとって「割安でクオリティが高い」銘柄です。この中から最終的な損益を決定づけるのは「割安でクオリティが高い」という事実ではなく、対象となった数ある銘柄の中からその時期にその銘柄をピックアップした、いわゆるあなたの指運だと言えます。それは例えば、たまたまその企業の名称が気に入ったものであるだとか、たまたまスクリーニングの一番上の段に表示されただとか、雑誌を読んで急に投資を思い立っただとか、そのような些細なことでしょう。

投資は、つまりガチャのようなもの

投資の結果は運に左右されます。というよりも殆どが運で決まるのです。ここでいう運とはすなわち上図のような確率分布のことを指します。上記の事例から分かるように投資の結果だけに着目すると、その投資そのものが上手くいったかどうかという判定は決してできないのです。あなたの行ったトレードの結果はご自身のスキルに依るものなのか偶然の産物なのか全く区別は付きません。

このように結果を正しく判断できないということは、その手法に依存してよいかどうかは分からないということです。つまりビジネスでいうところの改善のプロセスを回すことはできません。冒頭のようなアプローチでは、改善プロセスを回すという考え方が抜けているのです。改善プロセスを回すためには、定量的な結果を短期間でフィードバックさせる仕組みが必要となるのです。

もしも改善プロセスを回せないのであれば、あなたの投資スキルは未来永劫、決して向上することはないのです。これは中身の分からないガチャをひたすら回しているのと同じことです。あなたは年に数回ほど上記の排出分布の中から数個のガチャを引いて、その結果が良ければ歓喜し悪ければ落胆するという不毛な行為を繰り返しているにすぎないのです。

解決手段

ではこのガチャを攻略する方法はあるのでしょうか。当然、答えは「YES」です。

ガチャ台の素性

ガチャ台にも良いもの悪いものがあります。中身に当たりがたくさん入っている台は良い台、クズのようなものしか入っていない台は悪い台です。これらの台を排出分布で見てみるとどのようになっているのでしょうか。以下に3つの例を示します。
sample_hist.png

まず一番左の台はどうでしょうか。この台の排出分布の中心は殆ど0となっています。すわなちこの台の期待値は0です。この台でいくらガチャを引いてもあなたの資産が伸びることはないでしょう。一時的に利益が出ても、それは偶然パフォーマンスが上振れしているだけのことです。長い目でみると獲得した損益の平均値は必ず0に近づきます。

続いて真ん中の台はどうでしょう。この台の排出分布をよく見てみると、わずかに右側(プラス側)にシフトしていることが分かります。これは「期待値がプラス」である状態です。トレードでは、「エッジがある」「アルファ(期待超過収益)がある」などという表現をします。この台は、ガチャを引き続けていくことで右肩上がりに資産を伸ばすことができます。しかし道中の資産の上下変動は激しく、もしかするとあなたは途中でやめてしまうかもしれません。

最後に一番右の台です。この台の排出分布は真ん中の台と同じようにわずかに右側にシフトしています。そしてさらに目を凝らして見てみると、そのバラツキ(中心からの広がり)が真ん中の台と比べて小さな範囲に収まっていることが分かります。期待値がプラスで且つバラツキが小さい、すなわち「シャープレシオ」の高い台です。このようなガチャ台を見つけることができ、さらにそれを引き続けていくことであなたの成功は盤石なものとなるでしょう。

とにかく何回もガチャを回し続ける

ガチャを1回か2回引いた程度では、ガチャ台の中身がどのようなものなのか傾向を掴むことはできません。従ってもしも本当に中身の傾向を掴みたいのであれば、とにかく何回も何回も回し続けるしかありません。ここで重要なことは、わき目をふらず1つのガチャだけを回し続けるということです。途中まで引いて、こっちのガチャはあまり当たりが入ってなさそうだからこっちのガチャに替えてみる、というやり方では一生かかっても傾向を掴むことはできません。

つまりトレードでは、1つの手法だけに基づいて一貫してトレードを継続しなければならないのです。それも10回や100回そこらでは足りません。1000回を超えるような多数回施行をしなければならないのです。雑誌などの投資手法に次から次へと目移りしてはいけないのです。投資の持つ不確実性と戦うためには、とにかく試行回数を稼ぐしかありません。我々が持つ唯一の武器は大数の法則なのです。

従って、優位なトレーディング戦略とは「数をこなせる」戦略となります。個人投資家が行うトレードに月次以上のスパンのものは不適です。少なくともトレーディングは毎日行う必要があります。その最たるものはスキャルピングでしょう。また、トレーディングの期間が短くなればなるほど、価格の動きの不確実性は少なくなりその傾向を掴みやすくなるのです(具体的には投資の不確実性は投資期間の平方根に比例します)。

ガチャを引き続けるとお金がなくなっていく

しかし、当然の話ですがガチャを引くためにはお金が必要です。引けば引くだけあなたの財布からお金は消えていきます。これはトレードでも同じことです。トレードでは1回の取引に必ずコストが掛かります。株式であれば銘柄購入の際の手数料や信用金利、FXであれば1回のトレードでスプレッドを業者に支払っていることになります。何回もトレードを繰り返すとあっという間にあなたの資金は尽きてしまうでしょう。

実弾投入しながらガチャの中身の傾向を推定するのは、正直全く割りに合わない行為です。そんなことをするよりも、予め割の良いガチャ台に当たりを付ける方法があります。

データから予めガチャ台の中身を推定する

投資の世界では、我々は予めガチャ台に使われているデータの一部を集めることができます。そしてこのデータを使ってどのようなガチャ台の素性が良いのか、すなわちどのようなトレーディング戦略にベットすべきか、ガチャを引く前に検証することができます。これが計量的・実証的と呼ばれるトレーディング手法です。取引コストに打ち勝つことのできる期待値をデータ分析によって探すのです。

計量的・実証的なトレーディングに関する研究は長年行われています。特に最近では、これに機械学習を用いる事例も多数見掛けるようになりました。機械学習はデータの持つ統計的な有意性を確認できるだけではなく、誰も知らないような隠れた市場特性を抽出するためにも非常に有用です。プログラミングのライブラリは充実しており、データをダウンロードするためのAPIも整備されつつあります。個人投資家にも充分なデータ分析はできるのです。

そして運用へ

実際の運用では検証結果に基づいて可能な限り条件を固定して機械的にトレードを行わなければなりません。そこに人間の感情を挟んではいけないのです。そしてどうせ機械的に多数回のトレードを繰り返すのであれば、これを自動化しない理由はありません。このようなトレーディングスタイルは、包括的に「システムトレーディング」とも呼ばれています。

実際には、事前の検証(すなわちバックテスト)と実際の運用のパフォーマンスを比較しながら改善のプロセスを回すことが成功する投資のアプローチとなるのです。データ分析などしたことがないという人のために、以降の章ではPythonを使ったデータ分析の方法について紹介します。

計量的・実証的トレーディングの準備

さて、それではまずデータを集めます。データはYahoo FinanceのAPIを使って集めます。このときyfinanceというライブラリを使用しますので、まずこれをインストールします。yfinanceの詳細はこちらをご参照下さい。

pip install yfinance

(追記)yfinanceのバグについて
yfinanceにはバグがあり、デフォルトのままでは財務諸表データを取得できません。以下のようにyfinanceのbase.pyファイルを修正することで取得できるようになります。

# base.py 353行目付近
# get fundamentals
# data = utils.get_json(url+'/financials', proxy)    ←デフォルトプログラム。マスクします。
url = "{}/{}/financials".format(self._scrape_url, self.ticker)   # 追加
data = utils.get_json(url, proxy)                                # 追加

価格のヒストリカルデータ

では、まず日本の株式市場の銘柄について、価格データをダウンロードしてみましょう。

以下のコードを実行すると、一瞬で7203トヨタ自動車のヒストリカルデータを取得することができます。日本市場の銘柄のtickerシンボルは、証券コード+".T"で表されるため、特にYahoo Financeのシンボルを調べなくとも簡単にほぼ全ての銘柄のデータを取得することができます。

import yfinance as yf

ticker = yf.Ticker("7203.T")
hist = ticker.history(period="max")
print(hist)

<実行画面>

               Open     High      Low    Close    Volume  Dividends  Stock Splits
Date
1999-05-06  2259.74  2337.44  2233.84  2337.44   3115000        0.0             0
1999-05-07  2324.49  2330.96  2233.84  2253.27   3033000        0.0             0
1999-05-10  2253.27  2279.16  2233.84  2246.79   1261000        0.0             0
1999-05-11  2266.22  2279.17  2227.37  2227.37   1686000        0.0             0
1999-05-12  2227.37  2266.21  2227.37  2266.21   2596000        0.0             0
...             ...      ...      ...      ...       ...        ...           ...
2020-11-02  6866.00  7016.00  6850.00  6949.00   5721200        0.0             0
2020-11-04  7024.00  7054.00  6976.00  6976.00   6278100        0.0             0
2020-11-05  6955.00  7032.00  6923.00  6984.00   5643400        0.0             0
2020-11-06  7070.00  7152.00  7015.00  7019.00  11092900        0.0             0
2020-11-09  7159.00  7242.00  7119.00  7173.00   7838600        0.0             0

[5324 rows x 7 columns]

損益計算書

続いて財務諸表のデータを見てみましょう。まずは損益計算書からです。

以下のコードから直近およそ3年分の損益計算書を取得できます。この中で特に重要なのは、Total Revenue(売上高)、Operating Income(営業利益)、Net Income(当期純利益)でしょうか。

financials = ticker.financials
print(financials)

<実行画面>

                                         2020-03-31   2019-03-31   2018-03-31   2017-03-31
Research Development                           None         None         None         None
Effect Of Accounting Charges                   None         None         None         None
Income Before Tax                       2.82576e+12  2.64553e+12  3.09051e+12  2.55588e+12
Minority Interest                       6.77064e+11  7.18985e+11   6.9412e+11  6.68264e+11
Net Income                              2.07618e+12  1.88287e+12  2.49398e+12  1.83111e+12
Selling General Administrative          2.97317e+12   2.9867e+12   3.0905e+12  2.86848e+12
Gross Profit                            5.40763e+12   5.4439e+12  5.49036e+12  4.86286e+12
Ebit                                    2.43446e+12   2.4572e+12  2.39986e+12  1.99437e+12
Operating Income                        2.43446e+12   2.4572e+12  2.39986e+12  1.99437e+12
Other Operating Expenses                       None         None         None         None
Interest Expense                        -3.2217e+10  -2.8078e+10  -2.7586e+10  -2.9353e+10
Extraordinary Items                            None         None         None         None
Non Recurring                                  None         None         None         None
Other Items                                    None         None         None         None
Income Tax Expense                       6.8343e+11  6.59944e+11  5.04406e+11    6.289e+11
Total Revenue                             2.993e+13  3.02257e+13  2.93795e+13  2.75972e+13
Total Operating Expenses                2.74955e+13  2.77685e+13  2.69796e+13  2.56028e+13
Cost Of Revenue                         2.45224e+13  2.47818e+13  2.38892e+13  2.27343e+13
Total Other Income Expense Net          3.91297e+11   1.8833e+11   6.9065e+11  5.61513e+11
Discontinued Operations                        None         None         None         None
Net Income From Continuing Ops          2.14233e+12  1.98559e+12  2.58611e+12  1.92698e+12
Net Income Applicable To Common Shares   2.0589e+12  1.86808e+12  2.48169e+12  1.82131e+12

貸借対照表(バランスシート)

次は貸借対照表です。

以下のコードから直近およそ3年分の貸借対照表を取得できます。この中で特に重要なのは、Total Assets(総資産)、Total Liab(総負債)、Total Stockholder Equity(自己資本)でしょうか。

balance_sheet = ticker.balance_sheet
print(balance_sheet)

<実行画面>

                                    2020-03-31    2019-03-31    2018-03-31    2017-03-31
Capital Surplus                   4.893340e+11  4.871620e+11  4.875020e+11  4.840130e+11
Total Liab                        3.194275e+13  3.186981e+13  3.087815e+13  3.056711e+13
Total Stockholder Equity          2.006062e+13  1.934815e+13  1.873598e+13  1.751481e+13
Minority Interest                 6.770640e+11  7.189850e+11  6.941200e+11  6.682640e+11
Other Current Liab                4.102642e+12  4.479344e+12  4.399669e+12  3.979935e+12
Total Assets                      5.268044e+13  5.193695e+13  5.030825e+13  4.875019e+13
Common Stock                      3.970500e+11  3.970500e+11  3.970500e+11  3.970500e+11
Other Current Assets              2.469880e+11  1.425310e+11  2.022920e+11  1.235700e+10
Retained Earnings                 2.342761e+13  2.198752e+13  1.947346e+13  1.760107e+13
Other Liab                        2.746823e+12  2.887743e+12  2.902003e+12  3.163780e+12
Treasury Stock                   -4.253379e+12 -3.523575e+12 -1.622034e+12 -9.673210e+11
Other Assets                      8.905140e+11  1.182809e+12  1.067759e+12  1.012639e+12
Cash                              2.774498e+12  2.790212e+12  2.390524e+12  2.257064e+12
Total Current Liabilities         1.790238e+13  1.822694e+13  1.779689e+13  1.731896e+13
Deferred Long Term Asset Charges  3.547850e+11  5.018720e+11  4.941200e+11  5.039850e+11
Short Long Term Debt              1.418710e+11  1.560380e+11  1.674550e+11  2.285990e+11
Other Stockholder Equity         -1.166273e+12 -9.166500e+11  4.356990e+11  6.409220e+11
Property Plant Equipment          1.087864e+13  1.068549e+13  1.026767e+13  1.019711e+13
Total Current Assets              1.864253e+13  1.887924e+13  1.815266e+13  1.783370e+13
Long Term Investments             1.184489e+13  1.090829e+13  1.133854e+13  1.069452e+13
Net Tangible Assets               2.006062e+13  1.934815e+13  1.873598e+13  1.751481e+13
Short Term Investments            1.477202e+12  2.234892e+12  2.447703e+12  2.522598e+12
Net Receivables                   2.659748e+12  2.940890e+12  2.708900e+12  2.552805e+12
Long Term Debt                    1.029678e+12  7.655860e+11  5.910860e+11  5.784750e+11
Inventory                         2.434918e+12  2.656396e+12  2.539789e+12  2.388617e+12
Accounts Payable                  2.434180e+12  2.645984e+12  2.586657e+12  2.566382e+12

キャッシュフロー計算書

財務諸表の最後はキャッシュフロー計算書です。

以下のコードから直近およそ3年分のキャッシュフロー計算書を取得できます。この中で特に重要なのは、Total Cashflows From Operating Activities(営業キャッシュフロー)、Total Cashflows From Financing Activities(財務キャッシュフロー)、Total Cashflows From Investing Activities(投資キャッシュフロー)でしょうか。

cashflow = ticker.cashflow
print(cashflow)

<実行画面>

                                             2020-03-31    2019-03-31    2018-03-31    2017-03-31
Investments                                2.334300e+11  6.166420e+11 -3.322730e+11  6.950000e+08
Change To Liabilities                     -7.641000e+10  9.488700e+10  4.664800e+10  1.459570e+11
Total Cashflows From Investing Activities -3.150861e+12 -2.697241e+12 -3.660092e+12 -2.969939e+12
Net Borrowings                             1.558199e+12  7.229710e+11  6.893390e+11  1.030929e+12
Total Cash From Financing Activities       3.971380e+11 -5.408390e+11 -4.491350e+11 -3.751650e+11
Change To Operating Activities            -2.703900e+11  4.084000e+11  4.857250e+11  7.724320e+11
Net Income                                 2.076183e+12  1.882873e+12  2.493983e+12  1.831109e+12
Change In Cash                             7.056750e+11  4.868760e+11  7.031300e+10  2.098980e+11
Repurchase Of Stock                       -4.761290e+11 -5.496370e+11 -4.478180e+11 -7.039860e+11
Effect Of Exchange Rate                   -1.312450e+11 -4.164100e+10 -4.358800e+10 -1.348600e+10
Total Cash From Operating Activities       3.590643e+12  3.766597e+12  4.223128e+12  3.568488e+12
Depreciation                               1.605383e+12  1.792375e+12  1.734033e+12  1.610950e+12
Dividends Paid                            -6.299870e+11 -6.448060e+11 -6.268920e+11 -6.381720e+11
Change To Inventory                       -1.140960e+11 -1.669020e+11 -1.711480e+11 -2.463260e+11
Change To Account Receivables              2.488950e+11 -2.468450e+11 -1.054350e+11 -2.647840e+11
Other Cashflows From Financing Activities -5.494500e+10 -6.936700e+10 -6.376400e+10 -6.393600e+10
Change To Netincome                        2.228170e+11  1.431380e+11 -4.994310e+11 -1.598180e+11
Capital Expenditures                      -3.595131e+12 -3.738887e+12 -3.598707e+12 -3.541437e+12

銘柄のサマリー

最後に銘柄のサマリーの入手方法です。

以下のコードからその銘柄の基本情報を取得できます。この中で特に重要なのは、marketCap(時価総額)、sharesOutstanding(発行株数)、forwardPE(予測PER)、dividendYield(配当利回り)、profitMargins(純利益比率)など、様々なものがあります。

info = ticker.info
print(info)

<実行画面>
ディクショナリ型のため省略

複数銘柄の取得

複数銘柄を同時に取得する場合はTickersクラスを使い、引数はスペースで区切ります。

tickers = yf.Tickers("7203.T 9984.T 6861.T")
hists = []

for i in range(len(tickers.tickers)):
    hists.append(tickers.tickers[i].history())

print(hists[0])

<実行画面>

              Open    High     Low   Close    Volume  Dividends  Stock Splits
Date
2020-10-09  7026.0  7029.0  6947.0  6967.0   3395900          0             0
2020-10-12  6932.0  6945.0  6900.0  6911.0   2638200          0             0
2020-10-13  6977.0  7030.0  6946.0  7030.0   3667700          0             0
2020-10-14  6962.0  6970.0  6919.0  6935.0   3065400          0             0
2020-10-15  6898.0  6933.0  6895.0  6915.0   2844800          0             0
2020-10-16  6940.0  6944.0  6825.0  6829.0   3770200          0             0
2020-10-19  6874.0  6948.0  6870.0  6945.0   3047000          0             0
2020-10-20  6926.0  6945.0  6889.0  6897.0   2342400          0             0
2020-10-21  6962.0  7052.0  6956.0  7009.0   4795000          0             0
2020-10-22  6967.0  6984.0  6941.0  6966.0   3207500          0             0
2020-10-23  7009.0  7010.0  6944.0  6973.0   3963300          0             0
2020-10-26  6970.0  7003.0  6955.0  6990.0   2675000          0             0
2020-10-27  6970.0  6993.0  6924.0  6961.0   3234300          0             0
2020-10-28  6888.0  6927.0  6845.0  6895.0   3760200          0             0
2020-10-29  6795.0  6924.0  6780.0  6893.0   4099900          0             0
2020-10-30  6848.0  6878.0  6803.0  6803.0   5207800          0             0
2020-11-02  6866.0  7016.0  6850.0  6949.0   5721200          0             0
2020-11-04  7024.0  7054.0  6976.0  6976.0   6278100          0             0
2020-11-05  6955.0  7032.0  6923.0  6984.0   5643400          0             0
2020-11-06  7070.0  7152.0  7015.0  7019.0  11092900          0             0
2020-11-09  7159.0  7242.0  7119.0  7173.0   7838600          0             0

株価以外のデータ取得(為替)

Yahoo Financeに存在するティッカーであれば、株式銘柄でなくともデータを取得することができます。一例として為替のデータを取得してみましょう。

import pandas as pd

fxs = ["JPY=X", "EURUSD=X", "GBPUSD=X"]
tickers = yf.Tickers(" ".join(fxs))

closes = []
for i in range(len(tickers.tickers)):
    closes.append(tickers.tickers[i].history(period="max").Close)

df = pd.DataFrame(closes).T
df.columns = fxs

print(df)

<実行結果>

              JPY=X  EURUSD=X  GBPUSD=X
Date
1996-10-30  114.180       NaN       NaN
1996-11-01  113.500       NaN       NaN
1996-11-04  113.880       NaN       NaN
1996-11-05  114.250       NaN       NaN
1996-11-06  113.950       NaN       NaN
...             ...       ...       ...
2020-11-03  104.725    1.1643    1.2924
2020-11-04  104.546    1.1762    1.3122
2020-11-05  104.438    1.1733    1.2967
2020-11-06  103.603    1.1818    1.3139
2020-11-09  104.871    1.1910    1.3193

[6243 rows x 3 columns]

世界の主要株価指数

以下、世界の主要株価指数の取得方法です。ここに挙げたもの以外でも取れる指標はあります。ご自身でYahoo Financeで探してみることをお勧めします。

indices = ["^N225", "^DJI", "^GSPC", "^IXIC", "^GDAXI", "^FTSE", "^FCHI", "^HSI", "^SSEC", "^BVSP", "^KOSPI"]
# 以下、省略

計量的・実証的トレーディングの実行

では例題として本記事の問題提起で挙げた、割安でクオリティが高いと考えられる株(PERが10倍以下、ROEが10%以上)への投資したときのリターンの検証方法について、プログラムを元に解説します。なお、筆者のプログラミング能力は学生未満ですので、お見苦しい記述があるかと思います。コーディングに関する指摘があればコメント頂けると助かります。

銘柄リストの読み込み

まず東証の銘柄リストをCSVで準備します。東証の上場銘柄一覧はこちらから取得できますので検証したい銘柄をピックアップしてください。この例題では、TOPIXの中でも比較的大型であるTOPIX500構成銘柄を対象としました。

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

data = pd.read_csv("topix500.csv")
print(data)

<実行画面>

     code
0    1332
1    1333
2    1414
3    1605
4    1721
..    ...
494  9962
495  9983
496  9984
497  9987
498  9989

[499 rows x 1 columns]

ティッカーの設定

yfinanceのtickerを設定します。上記の銘柄にマーケットデータである日経平均も加えておきます。

stocks = [str(s)+".T" for s in data.code]
stocks.append("^N225")
tickers = yf.Tickers(" ".join(stocks))

終値データフレームの作成

続いてyfinanceで価格系列のヒストリカルデータを入手し、その中から終値のデータをデータフレームにまとめます。

closes   = [] # 終値

for i in range(len(tickers.tickers)):
    closes.append(tickers.tickers[i].history(period="max").Close)

closes = pd.DataFrame(closes).T   # DataFrame化
closes.columns = stocks           # カラム名の設定
closes = closes.ffill()           # 欠損データの補完

print(closes)

<実行画面>

            1332.T  1333.T  1414.T  1605.T  1721.T  1801.T  ...  9962.T   9983.T  9984.T  9987.T  9989.T     ^N225
Date                                                        ...
1965-01-05     NaN     NaN     NaN     NaN     NaN     NaN  ...     NaN      NaN     NaN     NaN     NaN   1257.72
1965-01-06     NaN     NaN     NaN     NaN     NaN     NaN  ...     NaN      NaN     NaN     NaN     NaN   1263.99
1965-01-07     NaN     NaN     NaN     NaN     NaN     NaN  ...     NaN      NaN     NaN     NaN     NaN   1274.27
1965-01-08     NaN     NaN     NaN     NaN     NaN     NaN  ...     NaN      NaN     NaN     NaN     NaN   1286.43
1965-01-12     NaN     NaN     NaN     NaN     NaN     NaN  ...     NaN      NaN     NaN     NaN     NaN   1288.54
...            ...     ...     ...     ...     ...     ...  ...     ...      ...     ...     ...     ...       ...
2020-11-04   417.0  2240.0  5200.0   526.0  2789.0  3325.0  ...  3135.0  74380.0  6535.0  3865.0  3945.0  23695.23
2020-11-05   417.0  2211.0  5220.0   506.0  2782.0  3335.0  ...  3200.0  74400.0  6870.0  3965.0  4020.0  24105.28
2020-11-06   421.0  2219.0  5270.0   507.0  2826.0  3385.0  ...  3245.0  75480.0  6722.0  3785.0  4155.0  24325.23
2020-11-09   423.0  2252.0  5360.0   500.0  3010.0  3440.0  ...  3360.0  78310.0  7083.0  3770.0  4185.0  24839.84
2020-11-10   437.0  2319.0  5410.0   541.0  3040.0  3535.0  ...  3455.0  77910.0  6860.0  3865.0  4145.0  25108.21

[13862 rows x 500 columns]

当期純利益データフレームの作成

次に財務諸表データをデータフレームにまとめます。まずはPERとROEを算出するための当期純利益です。各銘柄の決算期が揃っていないためNAN値が多いように見えますが、必要な箇所にはちゃんとデータが入っていますのでご安心ください。

earnings = [] # 当期純利益

dummy = tickers.tickers[0].financials.T["Net Income"]
dummy[:] = np.nan

for i in range(len(tickers.tickers)):
    try:
        earnings.append(tickers.tickers[i].financials.T["Net Income"])
    except:
        earnings.append(dummy)       # エラー発生時はダミーを入れる

earnings = pd.DataFrame(earnings).T  # DataFrame化
earnings.columns = stocks            # カラム名の設定

print(earnings)

<実行画面>

            1332.T  1333.T        1414.T  1605.T  1721.T  1801.T  ...  9962.T        9983.T  9984.T  9987.T  9989.T  ^N225
                                                                  ...
2006-08-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2007-08-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2009-03-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2010-03-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2011-03-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
...            ...     ...           ...     ...     ...     ...  ...     ...           ...     ...     ...     ...    ...
2020-05-20     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2020-05-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2020-06-30     NaN     NaN  9.005000e+09     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2020-08-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN  9.035700e+10     NaN     NaN     NaN    NaN
2020-09-30     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN

[69 rows x 500 columns]

自己資本データフレームの作成

次はROEを算出するための自己資本です。

equity   = [] # 自己資本

dummy = tickers.tickers[0].balance_sheet.T["Total Stockholder Equity"]
dummy[:] = np.nan

for i in range(len(tickers.tickers)):
    try:
        equity.append(tickers.tickers[i].balance_sheet.T["Total Stockholder Equity"])
    except:
        equity.append(dummy)         # エラー発生時はダミーを入れる

equity = pd.DataFrame(equity).T      # DataFrame化
equity.columns = stocks              # カラム名の設定

print(equity)

<実行画面>

            1332.T  1333.T        1414.T  1605.T  1721.T  1801.T  ...  9962.T        9983.T  9984.T  9987.T  9989.T  ^N225
                                                                  ...
2006-08-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2007-08-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2009-03-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2010-03-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2011-03-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
...            ...     ...           ...     ...     ...     ...  ...     ...           ...     ...     ...     ...    ...
2020-05-20     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2020-05-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2020-06-30     NaN     NaN  8.359900e+10     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN
2020-08-31     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN  9.565620e+11     NaN     NaN     NaN    NaN
2020-09-30     NaN     NaN           NaN     NaN     NaN     NaN  ...     NaN           NaN     NaN     NaN     NaN    NaN

[69 rows x 500 columns]

発行株数データフレームの作成

PERを算出するためにはEPS(一株益)が必要です。EPSを算出するために発行株数データフレームを作ります。

shares   = [] # 発行株数

for i in range(len(tickers.tickers)):
    try:
        shares.append(tickers.tickers[i].info["sharesOutstanding"])
    except:
        shares.append(np.nan)        # エラー発生時はNAN値を入れる

shares = pd.Series(shares)           # Series化
shares.index = stocks                # インデックス名の設定

print(shares)

<実行画面>

1332.T    3.111410e+08
1333.T    5.262460e+07
1414.T    5.382810e+07
1605.T    1.460200e+09
1721.T    1.260270e+08
              ...
9983.T    1.020820e+08
9984.T             NaN
9987.T    8.917480e+07
9989.T    1.169000e+08
^N225              NaN
Length: 500, dtype: float64

EPS、ROEデータフレームの作成

当期純利益、自己資本、発行株数のデータからEPS、ROEデータフレームを作ります。

eps = earnings/shares.values      # EPS
roe = earnings/equity             # ROE

eps = eps.ffill()                 # 欠損データの補完
roe = roe.ffill()

eps = eps.drop(["^N225"], axis=1) # ^N225カラムは削除しておく
roe = roe.drop(["^N225"], axis=1)

print(eps)
print(roe)

<実行画面>

               1332.T     1333.T      1414.T      1605.T  ...       9983.T  9984.T      9987.T      9989.T
                                                          ...
2006-08-31        NaN        NaN         NaN         NaN  ...          NaN     NaN         NaN         NaN
2007-08-31        NaN        NaN         NaN         NaN  ...          NaN     NaN         NaN         NaN
2009-03-31        NaN        NaN         NaN         NaN  ...          NaN     NaN         NaN         NaN
2010-03-31        NaN        NaN         NaN         NaN  ...          NaN     NaN         NaN         NaN
2011-03-31        NaN        NaN         NaN         NaN  ...          NaN     NaN         NaN         NaN
...               ...        ...         ...         ...  ...          ...     ...         ...         ...
2020-05-20  47.464013  238.23459  150.107472  107.557873  ...  1592.621618     NaN  316.378618  202.668948
2020-05-31  47.464013  238.23459  150.107472  107.557873  ...  1592.621618     NaN  316.378618  202.668948
2020-06-30  47.464013  238.23459  167.291805  107.557873  ...  1592.621618     NaN  316.378618  202.668948
2020-08-31  47.464013  238.23459  167.291805  107.557873  ...   885.141357     NaN  316.378618  202.668948
2020-09-30  47.464013  238.23459  167.291805  107.557873  ...   885.141357     NaN  316.378618  202.668948

[69 rows x 499 columns]

              1332.T    1333.T    1414.T   1605.T   1721.T  ...    9962.T    9983.T  9984.T    9987.T    9989.T
                                                            ...
2006-08-31       NaN       NaN       NaN      NaN      NaN  ...       NaN       NaN     NaN       NaN       NaN
2007-08-31       NaN       NaN       NaN      NaN      NaN  ...       NaN       NaN     NaN       NaN       NaN
2009-03-31       NaN       NaN       NaN      NaN      NaN  ...       NaN       NaN     NaN       NaN       NaN
2010-03-31       NaN       NaN       NaN      NaN      NaN  ...       NaN       NaN     NaN       NaN       NaN
2011-03-31       NaN       NaN       NaN      NaN      NaN  ...       NaN       NaN     NaN       NaN       NaN
...              ...       ...       ...      ...      ...  ...       ...       ...     ...       ...       ...
2020-05-20  0.096428  0.094528  0.103502  0.05165  0.08434  ...  0.078191  0.173209     NaN  0.068505  0.126817
2020-05-31  0.096428  0.094528  0.103502  0.05165  0.08434  ...  0.078191  0.173209     NaN  0.068505  0.126817
2020-06-30  0.096428  0.094528  0.107717  0.05165  0.08434  ...  0.078191  0.173209     NaN  0.068505  0.126817
2020-08-31  0.096428  0.094528  0.107717  0.05165  0.08434  ...  0.078191  0.094460     NaN  0.068505  0.126817
2020-09-30  0.096428  0.094528  0.107717  0.05165  0.08434  ...  0.078191  0.094460     NaN  0.068505  0.126817

[69 rows x 499 columns]

終値データフレームの整形、および月次リターンデータフレームの作成

ここからはゴリ押しでデータ整形していきます。まず月次データ用にデータ整形し、それから月次リターンデータフレーム(マーケットリターンを差し引いたもの)を作ります。

closes["month"] = closes.index.month                                      # 月カラムの作成
closes["end_of_month"] = closes.month.diff().shift(-1)                    # 月末フラグカラムの作成
closes = closes[closes.end_of_month != 0]                                 # 月末のみ抽出

monthly_rt = closes.pct_change().shift(-1)                                # 月次リターンの作成(ラグあり)
monthly_rt = monthly_rt.sub(monthly_rt["^N225"], axis=0)                  # マーケットリターン控除

closes = closes[closes.index > datetime.datetime(2017, 4, 1)]             # 2017年4月以降
monthly_rt = monthly_rt[monthly_rt.index > datetime.datetime(2017, 4, 1)]

closes = closes.drop(["^N225", "month", "end_of_month"], axis=1)          # 不要なカラムを削除
monthly_rt = monthly_rt.drop(["^N225", "month", "end_of_month"], axis=1)

print(closes)
print(monthly_rt)

<実行画面>

            1332.T   1333.T   1414.T   1605.T   1721.T   1801.T  ...   9861.T   9962.T    9983.T   9984.T   9987.T   9989.T
Date                                                             ...
2017-04-28  511.60  3064.04  2390.82   994.50  1964.87  3873.35  ...  1758.93  2063.02  35232.05  4138.08  3573.51  3686.69
2017-05-31  550.67  3049.61  2498.64   947.96  2172.28  4310.81  ...  1730.92  2443.18  35949.09  4413.07  3529.87  4063.84
2017-06-30  625.93  2855.29  2687.68  1006.13  2141.72  4675.36  ...  1810.12  2507.68  36259.17  4459.15  3617.15  3950.69
2017-07-31  613.54  2895.69  2763.53   998.68  2094.50  4812.06  ...  1801.43  2673.82  32092.56  4391.01  3573.51  3875.26
2017-08-31  589.73  3068.85  2886.77   978.21  2188.95  5026.24  ...  1818.65  2756.89  30664.60  4373.37  3883.83  4294.85

2020-07-31  435.19  2021.00  4530.00   599.10  3058.94  3557.00  ...  1791.22  2489.70  55837.62  6595.00  3713.65  3580.46
2020-08-31  471.87  2398.00  5010.00   673.80  2922.77  3601.22  ...  2098.00  2777.20  63280.00  6598.00  3907.01  3912.72
2020-09-30  447.00  2412.00  5220.00   563.50  2921.00  3550.00  ...  1970.00  2935.00  65860.00  6469.00  4005.00  3965.00
2020-10-30  401.00  2182.00  5020.00   492.00  2646.00  3245.00  ...  1915.00  3090.00  72710.00  6793.00  3765.00  3875.00
2020-11-10  437.00  2319.00  5410.00   541.00  3040.00  3535.00  ...  1998.00  3455.00  77910.00  6860.00  3865.00  4145.00

[44 rows x 499 columns]

              1332.T    1333.T    1414.T    1605.T    1721.T  ...    9962.T    9983.T    9984.T    9987.T    9989.T
Date                                                          ...
2017-04-28  0.052727 -0.028350  0.021457 -0.070438  0.081918  ...  0.160633 -0.003289  0.042813 -0.035853  0.078659
2017-05-31  0.117186 -0.083203  0.056174  0.041880 -0.033552  ...  0.006917 -0.010858 -0.009042  0.005243 -0.047327
2017-06-30 -0.014391  0.019553  0.033625 -0.002001 -0.016644  ...  0.071656 -0.109508 -0.009877 -0.006661 -0.013689
2017-07-31 -0.024808  0.073799  0.058595 -0.006498  0.059094  ...  0.045067 -0.030496  0.009982  0.100838  0.122273
2017-08-31 -0.013368  0.001479  0.016405  0.109973  0.112144  ...  0.018384  0.018514 -0.015500 -0.030392 -0.007134

2020-07-31  0.018428  0.120684  0.040103  0.058830 -0.110373  ...  0.049619  0.067429 -0.065402 -0.013790  0.026941
2020-08-31 -0.054665  0.003878  0.039956 -0.165659 -0.002566  ...  0.054860  0.038811 -0.021512  0.023120  0.011401
2020-09-30 -0.093937 -0.086386 -0.029343 -0.117915 -0.085175  ...  0.061782  0.112979  0.059056 -0.050954 -0.013728
2020-10-30  0.013155 -0.025898 -0.022349  0.013459  0.066692  ...  0.021076 -0.026613 -0.090432 -0.056213 -0.031199
2020-11-10       NaN       NaN       NaN       NaN       NaN  ...       NaN       NaN       NaN       NaN       NaN

[44 rows x 499 columns]

PER、ROEデータフレームの作成(月次リターンと同次元)

最後に月次リターンど同次元になるようにPER、ROEデータフレームを作成します。

eps_df = pd.DataFrame(index=monthly_rt.index, columns=monthly_rt.columns) # 月次リターンと同次元のDF作成
roe_df = pd.DataFrame(index=monthly_rt.index, columns=monthly_rt.columns)

for i in range(len(eps_df)):                                              # 各行への代入
    eps_df.iloc[i] = eps[eps.index < eps_df.index[i]].iloc[-1]

for i in range(len(roe_df)):
    roe_df.iloc[i] = roe[roe.index < roe_df.index[i]].iloc[-1]

per_df = closes/eps_df                                                    # PERデータフレームの作成

print(per_df)
print(roe_df)

<実行画面>

             1332.T   1333.T   1414.T   1605.T   1721.T 1801.T  ...   9861.T   9962.T   9983.T 9984.T   9987.T   9989.T
Date                                                            ...
2017-04-28  11.1972  10.4392      NaN   31.454  17.0954    NaN  ...  91.0625  31.8555      NaN    NaN  14.9553  18.4872
2017-05-31  12.0523    10.39      NaN   29.982     18.9    NaN  ...  89.6124  37.7256      NaN    NaN  14.7726  20.3785
2017-06-30  13.6995  9.72799      NaN  31.8218  18.6341    NaN  ...  93.7127  38.7215      NaN    NaN  15.1379  19.8111
2017-07-31  13.4284  9.86563  21.2599  31.5862  18.2232    NaN  ...  93.2628  41.2869      NaN    NaN  14.9553  19.4328
2017-08-31  12.9072  10.4556   22.208  30.9388   19.045    NaN  ...  94.1543  42.5696      NaN    NaN   16.254  21.5369

2020-07-31  9.16884  8.48323  27.0784  5.57002  14.8307    NaN  ...  162.317  42.8301  35.0602    NaN   11.738  17.6665
2020-08-31  9.94164  10.0657  29.9477  6.26453  14.1705    NaN  ...  190.117  47.7759  39.7332    NaN  12.3492   19.306
2020-09-30  9.41766  10.1245   31.203  5.23904  14.1619    NaN  ...  178.518  50.4906  74.4062    NaN  12.6589  19.5639
2020-10-30  8.44851  9.15904  30.0074  4.57428  12.8286    NaN  ...  173.534   53.157  82.1451    NaN  11.9003  19.1199
2020-11-10  9.20698   9.7341  32.3387  5.02985  14.7389    NaN  ...  181.056  59.4361  88.0198    NaN  12.2164  20.4521

[44 rows x 499 columns]

               1332.T     1333.T     1414.T     1605.T     1721.T  ...     9962.T     9983.T 9984.T     9987.T    9989.T
Date                                                               ...
2017-04-28   0.117515   0.153443        NaN  0.0156865   0.071603  ...    0.11847        NaN    NaN  0.0538158  0.170991
2017-05-31   0.117515   0.153443        NaN  0.0156865   0.071603  ...    0.11847        NaN    NaN  0.0538158  0.170991
2017-06-30   0.117515   0.153443        NaN  0.0156865   0.071603  ...    0.11847        NaN    NaN  0.0538158  0.170991
2017-07-31   0.117515   0.153443   0.101048  0.0156865   0.071603  ...    0.11847        NaN    NaN  0.0538158  0.170991
2017-08-31   0.117515   0.153443   0.101048  0.0156865   0.071603  ...    0.11847        NaN    NaN  0.0538158  0.170991

2020-07-31  0.0964277  0.0945276   0.107717    0.05165  0.0843397  ...  0.0781906   0.173209    NaN  0.0685051  0.126817
2020-08-31  0.0964277  0.0945276   0.107717    0.05165  0.0843397  ...  0.0781906   0.173209    NaN  0.0685051  0.126817
2020-09-30  0.0964277  0.0945276   0.107717    0.05165  0.0843397  ...  0.0781906  0.0944602    NaN  0.0685051  0.126817
2020-10-30  0.0964277  0.0945276   0.107717    0.05165  0.0843397  ...  0.0781906  0.0944602    NaN  0.0685051  0.126817
2020-11-10  0.0964277  0.0945276   0.107717    0.05165  0.0843397  ...  0.0781906  0.0944602    NaN  0.0685051  0.126817

[44 rows x 499 columns]

データの結合

それでは最後にこれらのデータフレームを1つにまとめます。

stack_monthly_rt = monthly_rt.stack()                                  # 1次元にスタック
stack_per_df = per_df.stack()
stack_roe_df = roe_df.stack()

df = pd.concat([stack_monthly_rt, stack_per_df, stack_roe_df], axis=1) # 結合
df.columns = ["rt", "per", "roe"]                                      # カラム名の設定

df["rt"][df.rt > 1.0] = np.nan                                         # 異常値の除去

print(df)

<実行画面>

                         rt      per        roe
Date
2017-04-28 1332.T -0.047638  11.1972   0.117515
           1333.T -0.070101  10.4392   0.153443
           1414.T  0.026680      NaN        NaN
           1605.T -0.038959   31.454  0.0156865
           1721.T  0.051664  17.0954   0.071603
...                     ...      ...        ...
2020-11-10 9962.T  0.025375  59.4361  0.0781906
           9983.T -0.021231  88.0198  0.0944602
           9984.T -0.082885      NaN        NaN
           9987.T -0.066187  12.2164  0.0685051
           9989.T -0.023070  20.4521   0.126817

[21892 rows x 3 columns]

対象銘柄の抽出とプロット

それでは割安でクオリティが高いと考えられる銘柄(PER<10、ROE>0.1)を抽出し、リターンの分布図およびこれらを売買したときの累積リターンを観察してみましょう。

このようにデータ分析を行うことで、雑誌などで紹介されるようなツールを使った個別銘柄選定が全く当てにならないことがよく分かります。この検証はあくまでもマーケットリターンを差し引いたものであるため、相場が好況なときには幾分リターンが得られているのかもしれません。

value_df = df[(df.per < 10) & (df.roe > 0.1)]       # 割安でクオリティが高い銘柄を抽出

plt.hist(value_df["rt"])                            # ヒストグラムの描画
plt.show()

balance = value_df.groupby(level=0).mean().cumsum() # 累積リターンを作成

plt.clf()
plt.plot(balance["rt"])                             # バランスカーブの描画
plt.show()

<実行結果>
balance.png

最後に

本記事で示した検証は、まだまだ計量的・実証的なトレーディングの入り口に過ぎません。この記事には「成功する投資」と銘打っていますが、実際にこの記事を読んで投資で成功する方は殆どいないでしょう。それは、記事を読んで頂いた方の大多数が行動にまで移すことがないからです。ここでいう行動とは、単にこの記事を読んで付け焼刃のトレーディングを行うことではなく、この記事から受けたインスピレーションを元に自らが創意工夫して計量的・実証的なトレーディングの検証を行うまでに至ることを指します。

この記事はできるだけ分かりやすくまとめていますが、それでも読者の方が単に記事を読んだだけでは、その過程に生じた細かで有用な知見を決して理解することはできません。これを自身のものにするためには、自分自身が手を動かすしかないのです。

筆者はこれまでにトレーディングに関する自身の知見をブログやnoteに書き綴ってきました。この記事を読んで、もしも読者の方が計量的・実証的なトレーディングに本気で取り組もうと考えているのであれば、以下の参考記事が必ずその道標になるでしょう。

繰り返しになりますが、トレーディングはその殆どが運で決まります。筆者がこれまで好成績を収めてこれたのも、そのパフォーマンスが運よく下振れすることがなかったからです。あなたにも幸運のご加護がありますように。

参考記事

blog_UKI
システムトレーダー。リターン予測には機械学習を使います。日本株はL/S安定運用、暗号通貨はbotによるリスク運用です。
https://note.com/trading/m/mdede34eae7fd
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント
(編集済み)

「投資は、つまりガチャのようなもの」と書かれていますが、経済指標のチェックやチャート分析やるかやらないかで勝率が全然違うと思うのでガチャとは思いません。
ガチャって自分の知識関係なくみんな同じ確率でガチャを引くことになるので、確率がだいぶ違ってくる気がします

書籍を例に取ると、甘い文句で投資を奨励するライトな入門書から金融の専門書までずらりと並びます。
投資の基礎について勉強したいのですが、おすすめの書籍はありますでしょうか。
沢山の書籍があり、どれを読むべきか迷っています。

本記事に感銘を受け、Pythonをインストールするところから実施しております。
自己資本データフレームの作成 セクションで以下のエラーが発生してしまうのですが、同じ状況になったり、本現象について情報をお持ちの方はおりますでしょうか。

■エラー内容
例外が発生しました: KeyError
'Total Stockholder Equity'

■エラー発生行
dummy = tickers.tickers[0].financials.T["Total Stockholder Equity"]

(編集済み)

@haihaikazuma san

tickers.tickers[0].balance_sheet.T["Total Stockholder Equity"]

ですね、検証はできていないですが

A-Koukiさん、仰るとおりです。スキルによってガチャの中の不良品を取り除くイメージです。

Junkyardさん、こちらは過去にブログでお勧めした書籍となります。
論理的な株式投資をするための書籍7選【初級編】

haihaikazumaさん、sidearrowさん、ご指摘ありがとうございます。私のタイプミスです。本文修正しておきました。

日本人が考案した一目均衡表など多くのテクニカル指標があり、ガチャのような単純な確率ではない高度な分析や、ノーベル賞を受賞した経済学者でも、リーマンショックや新型コロナなどは予測できません。
株価は往々にしてこのような事態に左右されます。
ただ、投資のプロたちは素人投資家の行動特性を熟知しているので、素人が勝つのは容易ではありません。
ゼロサムゲームでは負ける人がいなければ勝つ人が出てこないのです。

(編集済み)

初歩的な質問で恐縮ですが、
「東証の銘柄リストをCSVで準備します。東証の上場銘柄一覧はこちらから取得できます」のリンク先https://www.jpx.co.jp/markets/statistics-equities/misc/01.html 
の中に「topix500.csv」が探せないのですが、どこにありますでしょうか。ご教示いただけますと幸いです。

syun_nissiさん、リンクの「東証上場銘柄一覧」ファイル中の規模区分のうち、Core30、Large70、Mid400を合わせてTOPIX500となりますので、この銘柄コードをピックアップし、ご自身でCSVで保存してください。

銘柄の sharesOutstanding(発行済み株式数)ですが、時期によって変化しますかね?
(株式分割など)

もしそうであれば、時期ごとの sharesOutstanding を取得して計算する必要があると思いました。

@blog_UKIさん、どうもありがとうございました。エクセルデータを加工して自分でCSVファイルを作成するんですね。

(編集済み)

2020_11_22_21_11_12_b.py_yahoo_finance_Visual_Studio_Code.png

本題とはずれる質問になり恐縮ですが、上記プログラムを実行するととても時間がかかるのですが、対処方法などありますでしょうか。
→とても時間がかかると書きましたが、正確には実行できません(OUTPUTされません。)
 コードエラーになるわけではなく、結果が出ずに処理が終わらないのですが、何が原因かわかりません。お手数をかけて恐縮ですが、対応方法ご存知でしたらご教示ください。
よろしくお願いいたします。

creamsoda2015さん、株式分割はticker.splitsで取得可能です。厳密に検証を行う場合は当然考慮すべきです。

@blog_UKIさん
yfinanceのtickerの設定ですが、以下のようなエラーが出てしまいます。TOPIX500の銘柄リスト数を200ぐらいに削減するとエラーはでなくなります。500銘柄でエラーをださずに動作させるには何か設定みたいなものがあるのでしょうか?
対応方法等ご存知でしたらご教示いただけると幸いです。よろしくお願いいたします。
実行環境:Google Colaboratry

stocks = [str(s)+".T" for s in data.code]
stocks.append("^N225")
tickers = yf.Tickers(" ".join(stocks))
--------- error msg ----
File "", line 12
SyntaxError: more than 255 arguments

xyztrchさん、Pythonのversionによるものと思われます。Python3.7であれば255以上の引数を取ることができるはずです。必要に応じてアップデート等をご検討ください。

@blog_UKIさん
ご回答ありがとうございました。Google Colaboratory環境のpythonだどVersionが3.6.9のようなので、Python3.7環境で試してみます。

@blog_UKI
お疲れ様です。素晴らしい記事ですね。
質問させたいですが、
例えば、月初に購入して当月の月末の終値で販売戦略にすれば、 1ブランドは 一ヶ月に 1 トレードしかできないですかね?

あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした