Pythonを使って簡易MACDアルゴリズムを検証してみる

2022年6月12日日曜日

Python備忘録

t f B! P L

2022年6月12日 更新履歴

データフレームを取り扱うトレーニング

前回は、米国のテスラ社の株価と日本のトヨタ社の株価を2021年1月1日~2022年5月27日の株価データをYahooから読みだし、移動平均やMACDのテクニカル分析を行いました。

今回は、簡易的な売買アルゴリズムと、そのアルゴリズムに従った株価の売買を実施した際の損益を計算する検証ツールを作成します。

検証ツールがあれば、売買アルゴリズムの設計に集中できるし、先ずは、データフレームの扱いをトレーニングするつもりでご覧ください。

MACDの演算をモジュール化して汎用性を高める

短期指数、長期指数、MACDの指数をそれぞれ、short,long,macdというパラメータで受け渡し、あとで色々な指数で売買を実行してみようと思います。今回は、短期指数12、長期指数26、MACD指数9という一般的なパラメーターで実行しています。


import pandas_datareader.data as data
import pandas as pd
import numpy as np
import datetime 

st = '2021-01-01' 
ed = datetime.datetime.now()
us_stock = data.DataReader('TSLA','yahoo',st,ed)

def MACD(df,short,long,macd) :
    MACD = df
    MACD = MACD.drop(['High','Low','Open','Volume','Adj Close'], axis=1)
    MACD = MACD.rename(columns={'Close':'株価'})
    MACD['ShortEMA'] = MACD['株価'].ewm(span=short, adjust=False).mean()
    MACD['LongEMA'] = MACD['株価'].ewm(span=long, adjust=False).mean()
    MACD['MACD'] = MACD['ShortEMA'] - MACD['LongEMA']
    MACD['Signal'] = MACD['MACD'].ewm(span=macd,adjust=False).mean()
    
    return MACD

MACD = MACD(us_stock,12,26,9)
    

実行結果(MACDデータフレーム)は以下の通りです。


株価  ShortEMA  LongEMA MACD  Signal  
Date              
2020-12-31  705.669983  705.669983  705.669983  0.000000  0.000000  
2021-01-04  729.770020  709.377681  707.455171  1.922510  0.384502  
2021-01-05  735.109985  713.336497  709.503676  3.832821  1.074166  
2021-01-06  755.979980  719.897033  712.946365  6.950668  2.249466  
2021-01-07  816.039978  734.688255  720.582929  14.105326 4.620638  
... ... ... ... ... ... ... ...
2022-06-06  714.840027  729.369651  769.738128  -40.368478  -51.413420  
2022-06-07  716.659973  727.414316  765.806413  -38.392097  -48.809156  
2022-06-08  725.599976  727.135186  762.828158  -35.692972  -46.185919  
2022-06-09  719.119995  725.902080  759.590517  -33.688437  -43.686423 
2022-06-10  696.690002  721.407914  754.931219  -33.523305  -41.653799  
364 rows × 5 columns
    

MACDを使った初歩的な売買アルゴリズムを作ってみる

売買のタイミングは以下の通りです。

  • MACDがSignalを超えた(買いシグナル点灯)の翌日に成買い投入
  • MACDがSignalを下回った(売りシグナル点灯)の翌日に成売り投入

これを実現するため、MACDデータフレームに「買値」と「売値」の空データ(nan)を入れて用意しておき、MACDとSiganalがクロスした時に、us_stockの翌日(index[i+1]) の'Open'値を入力する様になります。

flagは、トレンドの方向性を示し、'0'で下降トレンド。'1'で上昇トレンド。最初はどちらから始まるかわからないので、初期値を'-1'としています。つまり「売り」からスタートすることもあるので検証ツール側で対応しています。


  def trade(MACD) :
    flag = -1
    MACD['買値'] = np.nan
    MACD['売値'] = np.nan
    for i in range(0,len(MACD)-1) :
        if MACD.at[str(MACD.index[i])[0:10],'MACD'] > MACD.at[str(MACD.index[i])[0:10],'Signal']:
            if flag == 0 :
                MACD.at[str(MACD.index[i+1])[0:10],'buy'] = us_stock.at[str(MACD.index[i+1])[0:10],'Open']
            flag = 1
        elif MACD.at[str(MACD.index[i])[0:10],'MACD'] < MACD.at[str(MACD.index[i])[0:10],'Signal']:
            if flag == 1 :
                MACD.at[str(MACD.index[i+1])[0:10],'sell'] = us_stock.at[str(MACD.index[i+1])[0:10],'Open']
            flag = 0
        else :
            pass  
    return MACD
  

もしも当日(今日)、MACDとSignalのクロスが発生すると、まだ用意できていない翌日(index[i+1])のデータを読みにいってエラーが発生してしまうので前日で処理が止まる様にfor文はrange(0,len(MACD)-1)としています。

compare_dateは、「買値」と「売値」の日付の前後関係をチェックするモジュールとなります。「売値」が先に来ていたら、「売値」を消して、常に「買値」→「売値」の順番になるように調整しています。

dropnaは、空データの行を消す関数で、「買値」が入った行だけを残したdf_buyデータフレームと「売値」が入った行だけを残したdf_sellデータフレームに分けて集計しています。

    
def compare_date(buy,sell,line) :
    
    buyDay = str(buy.index[line])
    sellDay = str(sell.index[line])
    
    buy_date = datetime.date(int(buyDay[0:4]), int(buyDay[5:7]), int(buyDay[8:10]))
    sell_date = datetime.date(int(sellDay[0:4]), int(sellDay[5:7]), int(sellDay[8:10]))

    if buy_date < sell_date :
        compare_result = True
    else :
        compare_result = False
        
    return compare_result


df_buy = MACD.dropna(subset=['buy'])
df_sell = MACD.dropna(subset=['sell'])

compare_result = compare_date(df_buy,df_sell,0)

if not compare_result :
    print('delete top of df_sell')
    df_sell = df_sell.drop(df_sell.index[0])
    
  

df_buyのデータフレームは以下の通りです。


  株価  ShortEMA  LongEMA MACD  Signal  buy sell
Date              
2021-03-15  707.940002  682.511950  713.631807  -31.119856  -37.675577  694.090027  NaN
2021-03-31  667.929993  650.116518  674.170400  -24.053883  -27.049232  646.619995  NaN
2021-05-27  630.849976  609.850086  630.708062  -20.857976  -25.777063  620.239990  NaN
2021-07-13  668.539978  662.842883  651.191190  11.651693 11.405484 686.320007  NaN
2021-08-02  709.669983  668.073834  659.092429  8.981405  5.079759  700.000000  NaN
2021-08-31  735.719971  709.426132  697.292239  12.133894 9.797616  733.000000  NaN
2021-09-28  777.559998  759.322573  742.458842  16.863731 15.062808 787.200012  NaN
2021-10-08  785.489990  777.149640  760.955110  16.194531 16.254005 796.210022  NaN
2021-10-13  811.080017  787.718074  769.706103  18.011972 16.726700 810.469971  NaN
2021-12-28  1088.469971 1020.703096 1023.387065 -2.683969 -14.426391  1109.489990 NaN
2022-01-13  1031.560059 1066.715098 1056.977137 9.737961  11.906165 1109.069946 NaN
2022-02-09  932.000000  925.901006  957.499236  -31.598231  -34.241718  935.000000  NaN
2022-02-16  923.390015  911.713725  938.760253  -27.046528  -31.846541  914.049988  NaN
2022-03-02  879.890015  858.873873  889.552889  -30.679016  -35.729036  872.130005  NaN
2022-03-17  871.599976  832.015790  853.048312  -21.032522  -27.633338  830.989990  NaN
2022-05-31  758.260010  728.482053  783.341565  -54.859512  -63.585998  773.840027  NaN
    

df_sellのデータフレームは以下の通りです。


  株価  ShortEMA  LongEMA MACD  Signal  buy sell
Date              
2021-03-30  635.619995  646.877704  674.669633  -27.791929  -27.798070  NaN 601.750000
2021-04-29  677.000000  709.030460  704.119302  4.911158  8.656600  NaN 699.510010
2021-07-12  685.700012  661.807047  649.803287  12.003760 11.343932 NaN 662.200012
2021-07-15  650.599976  659.727504  651.297521  8.429984  10.590903 NaN 658.390015
2021-08-17  665.710022  695.070968  684.222777  10.848190 14.016113 NaN 672.659973
2021-09-21  739.380005  741.976170  729.006247  12.969923 15.036459 NaN 734.789978
2021-10-07  793.609985  775.633213  758.992319  16.640894 16.268874 NaN 785.460022
2021-10-11  791.940002  779.425081  763.250287  16.174794 16.238163 NaN 787.650024
2021-11-11  1063.510010 1091.187309 1015.065071 76.122239 88.897272 NaN 1102.770020
2022-01-12  1106.219971 1073.106924 1059.010504 14.096420 12.448217 NaN 1078.849976
2022-01-14  1049.609985 1064.083542 1056.431423 7.652120  11.055356 NaN 1019.880005
2022-02-14  875.760010  907.256358  941.394663  -34.138305  -33.708402  NaN 861.570007
2022-02-23  764.039978  867.929821  908.914070  -40.984249  -33.289836  NaN 830.429993
2022-03-15  801.890015  822.016608  852.470914  -30.454307  -29.918049  NaN 775.270020
2022-04-11  975.929993  1034.072818 993.467041  40.605778 48.490256 NaN 980.400024
    

「買値」と「売値」から収益計算を行う

まずは、df_buyデータフレームとdf_sellデータフレームの長さを揃えて、一行づつ買った日と売った日の前後関係をチェックし、損益を計算します。

損益の計算は、一行づつ行いList形式で計算結果を蓄え、List形式をデータフレームに変換してから、to_html関数でHTMLのテーブル形式に変換します。


buySellList = []
total_gain = 0
write_table = 'html/revenue.html'

if len(df_buy) != len(df_sell) :
    df_buy = df_buy.drop(df_buy.index[len(df_buy)-1])
    print('Drop df_buy line : ',(len(df_buy)-1))
for line in range(len(df_buy)) :
    compare_result = compare_date(df_buy,df_sell,line)
    gain = int( df_sell.at[str(df_sell.index[line])[0:10],'sell'] - df_buy.at[str(df_buy.index[line])[0:10],'buy'] )
    total_gain = total_gain + gain
    if compare_result :
        tmp = [ str(df_buy.index[line])[0:10] , #買った日
                int(df_buy.at[str(df_buy.index[line])[0:10],'buy']) , #買った株価
                str(df_sell.index[line])[0:10] , #売った日
                int(df_sell.at[str(df_sell.index[line])[0:10],'sell']) , #売った株価
                gain , #損益
                round((gain * 100 / int(df_buy.at[str(df_buy.index[line])[0:10],'buy'])) , 1) , #損益率
                total_gain #トータル損益
              ]
    else :
        tmp = ['0000-01-01',0,'0000-01-01',0,0,0,0]
    buySellList.append(tmp)
    
cols = ['買った日', '買った株価','売った日', '売った株価','損益','損益率','トータル損益']
df_revenue = pd.DataFrame(buySellList,columns=cols)

f = open(write_table, 'w',encoding='utf-8')
try :
    f.write(df_revenue.to_html(index=False))
finally :
    f.close()
    

HTMLのテーブルは以下の通りです。

買った日 買った株価 売った日 売った株価 損益 損益率 トータル損益
2021-03-15 694 2021-03-30 601 -92 -13.3 -92
2021-03-31 646 2021-04-29 699 52 8.0 -40
2021-05-27 620 2021-07-12 662 41 6.6 1
2021-07-13 686 2021-07-15 658 -27 -3.9 -26
2021-08-02 700 2021-08-17 672 -27 -3.9 -53
2021-08-31 733 2021-09-21 734 1 0.1 -52
2021-09-28 787 2021-10-07 785 -1 -0.1 -53
2021-10-08 796 2021-10-11 787 -8 -1.0 -61
2021-10-13 810 2021-11-11 1102 292 36.0 231
2021-12-28 1109 2022-01-12 1078 -30 -2.7 201
2022-01-13 1109 2022-01-14 1019 -89 -8.0 112
2022-02-09 935 2022-02-14 861 -73 -7.8 39
2022-02-16 914 2022-02-23 830 -83 -9.1 -44
2022-03-02 872 2022-03-15 775 -96 -11.0 -140
2022-03-17 830 2022-04-11 980 149 18.0 9

  • 最高値が36.0%増の292
  • 最低値が13.3%減の-92

今回は比較的値動きの激しいテスラの株価で検証しましたが、値動きの小さい株価で検証したり、MACDのパラメーターを変更したり、平均移動やRSIのデータ分析を加えたり、利益を増やす手段は無限大ですよ~(^^♪

更新履歴

2022.06.12
新規作成

QooQ