市場はランダムではないという証明を自分でも確認してみました

『ラリー・ウィリアムズの短期売買法【改定第2版】』を読み直しています。 “市場はランダムではない”という証明を自分でもやってみました。

環境

  • Ubuntu 18.04 LTS
  • Python 3.6
  • Jupyter Notebook 5.2.0
  • NumPy 1.14.4
  • pandas 0.23.0
  • matplotlib 2.2.2

市場はランダムではない

価格の動きは高いランダム性を含んではいるが、完全にランダムというわけではない。 これを証明できなければ、本書の残りはダーツの投げ方の学習に当てたほうがよいかもしれない。 ランダムなゲームでは、専門家の予想よりもダーツで投げて決めたほうが高いパフォーマンスを上げることができるのだから。

それでは証明を始めよう。 コインを100回投げたら、50回は表が出て、50回は裏が出るものと仮定する。 表が出ても裏が出ても、次にコインを投げたときに表と裏が出る確率はそれぞれ50%で変わらない。 ご存知かもしれないが、コイン、サイコロ、ルーレットは記憶を持たない。 これはランダムなゲームなので確率は一定なのである。

これが市場にも当てはまり、上げて引ける確率が常に50%であるとするならば、上げて引けた場合、次に上げて引ける確率は50%で、次に上げて引けた場合、次に下げて引ける確率は50%になる。 2回続けて下げて引けた場合も、次に下げて引ける確率は50%だ。 しかし、実際のトレーディングではこうはならない。 これは、価格の動きが完全にランダムではない証拠にほかならない。

表 1.1 はさまざまな市場において価格が上げて引ける確率を示したものだ。

表 1.1 各銘柄--始値より上げて引ける確率

銘柄 確率
ポークベリー 51
綿花 53
大豆 51
小麦 52
英ポンド 56
52
日経 55
ユーロドル 57
T ボンド 52
S&P500 53
平均 53.2

何の基準も設けずに、コンピューターには寄り付きで買わせ、引けで手仕舞いさせた。 寄り付きよりも上げて引けた確率は 50% ではなく、 53.2% と若干のひずみがある。 価格の動きがランダムならこうはならないはずだ。

「こうはならないはず」というのなら、下げて引けた翌日に寄り付きで買ってみてはどうか。 理論的には、表 1.1 の上げて引ける確率はすべて同じく 50% になるはずである。 理論には強いが、市場の知識にはうとい大学教授や学術研究者たちにとって、問題は実際にはこうならないことである。 表 1.2 はそれぞれの銘柄が 1 日か、 2 日連続して下げて引けたあと、上げて引けた回数と確率を示したものだ。

表 1.2 各銘柄--1 日か 2 日下げて引けたあと、上げて引けた確率

銘柄 確率 1 確率 2
ポークベリー 55 55
綿花 53 55
大豆 56 56
小麦 53 55
英ポンド 57 56
58 55
日経 56 60
ユーロドル 59 56
T ボンド 54 52
S&P500 55 53
平均 55.8 55.2

ラリー・ウィリアムズの短期売買法【改定第2版】 第 1 章 短期のカオスのなかに秩序を見いだす

ランダムなデータは 50% になって、市場のデータは 50% にならないことを確認してみます。

ヒストリカルデータ

以前の記事で作った日足のデータを使います。

米ドル/円

ここから Jupyter Notebook のセルに入力しながら進めます。

最初に必要なモジュールを読み込みました。

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

ヒストリカルデータを読み込みました。

dtype = { 'time': str, 'open': float, 'high': float, 'low': float, 'close': float, 'volume': float }
names = ['time', 'open', 'high', 'low', 'close', 'volume']
df = pd.read_csv('~/Documents/data/d/USDJPY_D.csv', dtype=dtype, header=0, index_col='time', names=names, parse_dates=['time'])
df.head()

上がるか下がるかだけ分かればいいので、 1 日の値幅を計算しました。

s = df['close'] - df['open']
s.head()

最初に、上げて引けた確率です。

s[s > 0].count() / s.count()

結果です。

0.4988241442383068

50% よりわずかに少ないみたいです。

次に、 1 日下げて引けたあと、上げて引けた確率です。

s1 = s.shift(1)
s[(s1 < 0) & (s > 0)].count() / s[(s1 < 0)].count()

結果です。

0.5237341772151899

52.3% になりました。 2% だけ偏るっていうのはすごいことなのかな?

最後に、 2 日連続して下げて引けたあと、上げて引けた確率です。

s2 = s.shift(2)
s[(s2 < 0) & (s1 < 0) & (s > 0)].count() / s[(s2 < 0) & (s1 < 0)].count()

結果です。

0.5335570469798657

53.3% になりました。 また少し上がりました。

21 通貨ペアの平均も確認してみました。 次のようになりました。

  • 上げて引けた確率: 50.2%
  • 1 日下げて引けたあと、上げて引けた確率: 51.5%
  • 2 日連続して下げて引けたあと、上げて引けた確率: 52.0%

ラリー・ウィリアムズが確認したみたいに 55% まで高くありませんでした。 ユーロ/豪ドル、米ドル/香港ドルがともに 45% 程度になっていました。 これが足を引っ張っていたのかもしれません。 けれども 60% になるものもなかったので、やっぱりそれほど高くはないのかもしれません。

ランダムなデータ

ランダムなデータを作りました。 サイズは 5000 にしました。

s = pd.Series(np.random.choice([1, -1], size=5000))
s.head()

最初に、上げて引けた確率です。

s[s > 0].count() / s.count()

結果です。

0.493

50% にはなりませんでした。

次に、 1 日下げて引けたあと、上げて引けた確率です。

s1 = s.shift(1)
s[(s1 < 0) & (s > 0)].count() / s[(s1 < 0)].count()

結果です。

0.4905288082083662

49.0% になりました。 下がりました。

最後に、 2 日連続して下げて引けたあと、上げて引けた確率です。

s2 = s.shift(2)
s[(s2 < 0) & (s1 < 0) & (s > 0)].count() / s[(s2 < 0) & (s1 < 0)].count()

結果です。

0.5011618900077459

50.1% になりました。 今度は上がりました。 ランダムな結果になっていい感じです。

これを 20 回やって平均してみました。 結果はそれぞれ次のようになっていました。

  • 上げて引けた確率: 50.1%
  • 1 日下げて引けたあと、上げて引けた確率: 50.3%
  • 2 日連続して下げて引けたあと、上げて引けた確率: 50.4%

20 回の平均はわずかに上がっていっていました。 けれどもランダムなデータはどこまで続けて下げたとしても次に上がる確率は 50% になりそうです。

終わり

これはトレーダーにとっては驚くほどのことではない。 私たちは市場は下がれば上がることを知っているからだ。 これまで正確な確率は分からなかったが、たとえ分かっていたとしても、私はポジションを建てたり保持したりするのにこうした表を使うことはないだろう。 重要なのは、 1 日下げて引けたあと、そして 2 日連続して下げて引けたあと、次に上げて引ける確率は 53.2% になるはずなのにそうはならなかったという事実である。 これは市場がランダムではないことを物語っている。 つまり、価格の動きにはパターンがあり、そのパターンから動きを「予測」できるわけである。 これでダーツの投げ方を学習する必要はなくなった。

ラリー・ウィリアムズの短期売買法【改定第2版】 第 1 章 短期のカオスのなかに秩序を見いだす

2% でも下がるより上がる確率が高ければそれを活用することができるのかな? ちょっとまだ 2% 程度で口座残高にどう影響が出るのかイメージできないです。 上がるか下がるかの確率だけじゃなくて、値幅も影響すると思いますし、そう簡単に言えることじゃないのかもしれません。

Gist に Jupyter Notebook をアップロードしました。