auカブコム証券APIとPythonでトレードアプリ開発。
初心者向けにアプリ開発方法を解説します。
第5回目は、いよいよトレード戦略について解説をします。
2本の移動平均線を使った戦略です。
おなじみの、移動平均線交差によるトレードで利益が出るのか?
検証や、最適な移動平均線の組み合わせをPythonで算出します。
第5回動画解説 移動平均交差戦略と最適化。
動画の目次
【目次】
01:18 前回までの動画の内容振り返り
05:15 本編スタート トレード戦略
08:21 このセクションで、やりたい事の概要説明
11:30 利益算出
12:35 利益率算出
15:54 平均利益率算出
21:26 損益累積和を算出
23:28 移動平均線交差戦略シミュレート
26:06 ベクトル化バックテスト
30:30 移動平均組み合わせの最適化
37:27 過剰最適化
解説
動画で解説したPythonコード
動画で解説したPythonコードはこちらです。
「新しく覚えることを最小にして、動くものを作る」これを最優先としています。
初心者が理解しやすいように、コード記述の美しさや、推奨されている記述ルールよりも、単純であることを優先しました。
コードの合間に広告が出てジャマかもしれません。
私の知識不足でコードの間の広告を消すことができません。
コードが表示されているエリアにマウスポインターを移動させると、表示エリア上部にアイコンが表示されます。
アイコンをポチポチすると表示が変わったり、コードをコピーすることもできますので、お試しください。
◇移動平均線交差戦略をトヨタ自動車の株価に適応する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
st.markdown('# トレード戦略') st.markdown('## 移動平均線交差') if st.button('トヨタ自動車の日足に適応する。'): Security_code = "7203.T" #plotlyをインポート import plotly.graph_objects as go # 株価をインターネット上から取得するライブラリーをインポート from pandas_datareader import data as web import numpy as np # df_Security_code = web.DataReader(Security_code, data_source='yahoo', start='2000-1-1') df_Security_code = web.DataReader(Security_code, data_source='yahoo', start='2000-1-1', end='2021-6-30') # 日付がインデックスだとplotlyで描画されないようなので、インデックスから外す df_Security_code_ri = df_Security_code.reset_index() st.write('取得したデーター') st.dataframe(df_Security_code_ri) # 日毎の損益を算出 '日毎の損益' df_Security_code_ri['simple_row_return'] = (df_Security_code_ri['Adj Close'] - df_Security_code_ri['Adj Close'].shift(1)) st.dataframe(df_Security_code_ri[['Date', 'Adj Close', 'simple_row_return']]) # 日毎の損益率を算出 df_Security_code_ri['simple_return'] = (df_Security_code_ri['Adj Close'] / df_Security_code_ri['Adj Close'].shift(1)) - 1 # print(PG['simple_return']) '日毎の損益と単純利益率' st.dataframe(df_Security_code_ri[['Date', 'Adj Close', 'simple_row_return', 'simple_return']]) '日毎の損益をグラフ化' # 初期化 fig7 = go.Figure() fig7.add_trace(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['simple_return'])) # チャートのタイトルを表示 fig7.update_layout(title_text='日毎の単純利益率(simple_return)') # fig.show() st.plotly_chart(fig7, use_container_width=True) '平均利益率' avg_returns_d = df_Security_code_ri['simple_return'].mean() st.write(avg_returns_d) '年間利益率(250日)' avg_returns_a = df_Security_code_ri['simple_return'].mean() * 250 st.write(avg_returns_a) '年間利益率のパーセント表示' # 組み込み関数のround:数値を指定した桁数に丸める # roundは四捨五入ではなく偶数への丸め。 # strで文字列に変換して % を 付ける avg_returns_a_round = str(round((avg_returns_a * 100), 5)) + '%' st.write(avg_returns_a_round) # 移動平均を追加 # 移動平均の値を指定 # SMA_s = 20 # SMA_l = 250 # SMA_s = 50 # SMA_l = 100 SMA_s = 50 SMA_l = 95 # 移動平均を算出してデーターフレームに書き込む df_Security_code_ri['SMA_S'] = df_Security_code_ri['Adj Close'].rolling(SMA_s).mean() df_Security_code_ri['SMA_L'] = df_Security_code_ri['Adj Close'].rolling(SMA_l).mean() 'データーフレームに移動平均を追加' st.write(df_Security_code_ri) # 初期化 fig8 = go.Figure() st.write('調整後終値に移動平均線を追加') fig8.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['Adj Close'], name='Adj Close')) # 移動平均線を追加 fig8.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['SMA_S'], name='短期移動平均')) fig8.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['SMA_L'], name='長期移動平均')) # チャートのタイトルを表示 fig8.update_layout(title_text=Security_code + ':調整後終値と移動平均') #スライドバー非表示 fig8.update_layout(xaxis_rangeslider_visible=False) # fig.show() # st.plotly_chart(fig8, use_container_width=True) st.plotly_chart(fig8) # 日毎損益の累積和 df_Security_code_ri['Accumulation_row_return'] = df_Security_code_ri['simple_row_return'].cumsum() '日毎の損益の累積和' st.dataframe(df_Security_code_ri[['Date', 'Adj Close', 'simple_row_return', 'Accumulation_row_return']]) # 初期化 fig9 = go.Figure() st.write('調整後終値に移動平均線を追加') fig9.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['Adj Close'], name='Adj Close')) # チャートの凡例に移動平均の値を表示させるために、文字列に変換して変数に代入する。 str_SMA_s = str(SMA_s) str_SMA_l = str(SMA_l) # 移動平均線を追加 fig9.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['SMA_S'], name='短期移動平均'+str_SMA_s)) fig9.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['SMA_L'], name='長期移動平均'+str_SMA_l)) fig9.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['Accumulation_row_return'], name='累積和')) # チャートのタイトルを表示 fig9.update_layout(title_text=Security_code + ' : 調整後終値・移動平均・累積和') # スライドバー表示 fig9.update_layout(xaxis_rangeslider_visible=True) # fig.show() # st.plotly_chart(fig8, use_container_width=True) st.plotly_chart(fig9) st.markdown('## 移動平均線交差で戦略のシミュレート') ''' 移動平均交差トレードのルール: 短期移動平均線が長期移動平均線より上ならば「買い」 短期移動平均線が長期移動平均線より下ならば「売り」 ''' # もし、短期移動平均線['SMA_S']が長期移動平均線['SMA_L']よりも大きければ 1 そうで無ければ -1 # np.where( 短期移動平均線['SMA_S'] > 長期移動平均線['SMA_L'] ならば 1 、 そうで無ければ -1 df_Security_code_ri['Position'] = np.where(df_Security_code_ri['SMA_S'] > df_Security_code_ri['SMA_L'], 1, -1) '売買フラグ' st.dataframe(df_Security_code_ri[['Date', 'Adj Close', 'SMA_S', 'SMA_L', 'Position']]) # データフレームの欠損値を含むデーターを削除 df_Security_code_ri.dropna(inplace=True) '欠損値が削除されている' st.dataframe(df_Security_code_ri[['Date', 'Adj Close', 'SMA_S', 'SMA_L', 'Position']]) # ベクトル化バックテスト st.markdown('## ベクトル化バックテスト・移動平均線交差') df_Security_code_ri['Strategy'] = df_Security_code_ri['Position'].shift(1) * df_Security_code_ri['simple_row_return'] st.dataframe(df_Security_code_ri[['Date', 'Adj Close', 'simple_row_return', 'Position', 'Strategy']]) '移動平均交差トレードの累積和' df_Security_code_ri['Accumulation_Strategy'] = df_Security_code_ri['Strategy'].cumsum() st.dataframe(df_Security_code_ri[['Date', 'Adj Close', 'simple_row_return', 'Position', 'Strategy', 'Accumulation_Strategy']]) # データーをエクセルに書き出す df_Security_code_ri.to_excel('z20210701Optimization.xlsx') # 初期化 fig10 = go.Figure() st.write('調整後終値に移動平均線を追加') fig10.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['Adj Close'], name='Adj Close')) # チャートの凡例に移動平均の値を表示させるために、文字列に変換して変数に代入する。 str_SMA_s = str(SMA_s) str_SMA_l = str(SMA_l) # 移動平均線を追加 fig10.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['SMA_S'], name='短期移動平均'+str_SMA_s)) fig10.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['SMA_L'], name='長期移動平均'+str_SMA_l)) fig10.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['Accumulation_row_return'], name='累積和')) fig10.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['Accumulation_Strategy'], name='移動平均クロス累積')) # fig10.add_traces(go.Scatter(x=df_Security_code_ri['Date'], y=df_Security_code_ri['Position'], name='売り買いポジション', yaxis='y2')) # チャートのタイトルを表示 fig10.update_layout(title_text=Security_code + ' : 移動平均クロス戦略累積和') #スライドバー表示 fig10.update_layout(xaxis_rangeslider_visible=True) st.plotly_chart(fig10) |
◇移動平均線交差の最適化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
st.markdown('## 移動平均の組み合わせ最適化') if st.button('移動平均の組み合わせ最適化を実行する。'): from itertools import product import pandas as pd Security_code = "7203.T" # 株価をインターネット上から取得するライブラリーをインポート from pandas_datareader import data as web import numpy as np df_Security_code = web.DataReader(Security_code, data_source='yahoo', start='2000-1-1', end='2021-6-30') # range関数で移動平均の値を作成 # 書式:range(start,stop[,step]) SMA_s = range(5, 51, 5) SMA_l = range(75, 301, 5) # range関数で取得した値を総当たりで試す # 総当たりの結果を格納する空のデーターフレームを作成 results = pd.DataFrame() # このループですべての組み合わせを試して、結果をデーターフレームに格納する for SMA1, SMA2 in product(SMA_s, SMA_l): data = pd.DataFrame(df_Security_code['Adj Close']) # 欠損値を削除 # 引数inplace=Trueを指定すると元のオブジェクト自体が変更される。 data.dropna(inplace=True) data['simple_row_return'] = data['Adj Close'] - data['Adj Close'].shift(1) data['SMA1'] = data['Adj Close'].rolling(SMA1).mean() data['SMA2'] = data['Adj Close'].rolling(SMA2).mean() data.dropna(inplace=True) data['Position'] = np.where(data['SMA1'] > data['SMA2'], 1, -1) data['Strategy'] = data['Position'].shift(1) * data['simple_row_return'] # 欠損値を削除 # 引数inplace=Trueを指定すると元のオブジェクト自体が変更される。 data.dropna(inplace=True) perf = data[['simple_row_return', 'Strategy']].sum() # pandas.DataFrameに新たな行を追加するために、append()メソッドを使用する。 results = results.append(pd.DataFrame( {'SMA1': SMA1, 'SMA2': SMA2, 'SIMPLE_ROW_RETURN': perf['simple_row_return'], 'STRATEGY': perf['Strategy'], 'OUT': perf['Strategy'] - perf['simple_row_return']}, index=[0]), ignore_index=True) # 最適化データーを表示する st.dataframe(results) |
最適化コード作成時に参考にさせていただいた書籍
Pythonによるファイナンス第2版
発行:オライリージャパン
著者:Yves Hilpisch
疑問を解決するためにインターネット上をさまよい歩いた後、「なんだ、この本に書いてあったじゃん」なんて思いを何度もしています。
購入時の定価は4500円+税
Pythonで株価分析を目指すなら、おススメです。
ただし、Pythonとファイナンスの基礎知識がないと、難しく感じるかもしれません。
この記事を書いた人
あしおゆたか
投資歴21年の個人投資家
机上の理論ではなく、実体験に基づいた記事作りをモットーにしています。
スポーツクラブに毎週2日~3日通い、サウナ後の暴飲暴食が趣味。(現在自粛中)
◇主な投資対象
日本株式
株式ETF(上場投資信託)
日経225先物
日経225先物オプション
◇運営者情報はこちら