前回は、「PythonでFXのヒストリカルデータを元にボリンジャーバンドを描写する」で
ヒストリカルデータを元にボリンジャーバンドを描写してみました。
で、テクニカル指標を描写しているだけでは面白くないので
テクニカル指標を使ってトレードを行うとどんな結果になるのか
シミュレーションを行ってみました。
手元にあるFXのヒストリカルデータを元にシミュレーションしますので、
注文処理も、利益確定・ストップロス処理も疑似的に実装します。
10万円の資金で始めて、1万通貨単位で建玉は一つだけ
(ポジションを持っている場合は注文をしない)
という条件でシミュレーションしてみました。
シミュレーションと言いつつ、非常に簡易的なものですので、
以下の点について注意が必要です。
注文を入れた金額で必ず約定している
取引手数料(スプレッドも)は一切考慮していない
スワップポイントは一切考慮していない
上記については、実際の取引では考慮する必要がありますので
今回のシミュレーションの結果は、実際の取引と異なると思われますが、
「テクニカル指標を使ってどのぐらい勝てそうか」という
傾向を見るために実施してみました。
今回使用したテクニカル指標はボリンジャーバンドのみです。
±2σに価格が近づくと、反発するだろうという性質を利用したものです。
データは1分足を使用しました。
ロジックとしては、現在価格が25分移動平均の±2σに到達した際に
買い・売り注文を入れ、約定価格から30pips移動したら
利確・損切をするというものです。
利確(予想通りに値動きした場合)でも、
損切(予想と逆に値動きした場合)でも
30pips値動きしたら注文を閉じます。
2016年1月1日00時00分から2017年1月1日00時00分のデータで実行しました。
まずはプログラムソースです。
(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 |
# -*- coding: utf-8 -*- """ Created on Tue Jan 10 16:44:16 2017 @author: ichizo """ import datetime import sqlite3 import pandas as pd import matplotlib.pyplot as plt from matplotlib.finance import candlestick_ohlc DB = 'usd_jpy_prices.db' FROMDATE = '20160101000000' TODATE = '20170101000000' DTYPE = '5min' UNIT = 10000 #1万通貨単位 DIFF_PRICE = 0.30 #価格差30pipsで利確or損切 ASSET = 100000 #10万円でスタート def get_datas(): conn = sqlite3.connect(DB) sql = "SELECT datetime, open, high, low, close FROM onemin WHERE datetime >= '%s' AND datetime <= '%s';" % (FROMDATE, TODATE) datas = pd.io.sql.read_sql_query(sql, conn) conn.close() return datas def convert_datas(datas): datas['open'] = datas['open'].astype(float) datas['high'] = datas['high'].astype(float) datas['low'] = datas['low'].astype(float) datas['close'] = datas['close'].astype(float) if DTYPE == '1min': tmp = datas['datetime'].values.astype('datetime64[D]') datas['datetime'] = tmp.astype(float) return datas quotes_arr = [] tmp_open_price = 0 tmp_high_price = [] tmp_low_price = [] tmp_close_price = 0 count = 0 for loop, cDate in enumerate(datas['datetime']): isNext = False cDatetime = datetime.datetime.strptime(cDate, "%Y%m%d%H%M%S") if DTYPE == '5min' and cDatetime.minute % 5 == 0: isNext = True if DTYPE == '10min' and cDatetime.minute % 10 == 0: isNext = True if DTYPE == '30min' and cDatetime.minute % 30 == 0: isNext = True if DTYPE == '1hour' and cDatetime.minute == 0: isNext = True if DTYPE == '1day' and cDatetime.hour == 0: isNext = True if tmp_open_price == 0: tmp_open_price = datas['open'][loop] if isNext == True and loop != 0: data = [] data.append(cDate) data.append(tmp_open_price) data.append(max(tmp_high_price)) data.append(min(tmp_low_price)) data.append(tmp_close_price) pddata = pd.DataFrame([data]) pddata.columns = ["datetime", "open", "high", "low", "close"] pddata.index = [count] quotes_arr.append(pddata) tmp_open_price = datas['open'][loop] tmp_high_price = [] tmp_low_price = [] count += 1 tmp_high_price.append(datas['high'][loop]) tmp_low_price.append(datas['low'][loop]) tmp_close_price = datas['close'][loop] for idx, value in enumerate(quotes_arr): if idx == 0: quotes = quotes_arr[idx] else: quotes = pd.concat([quotes, value],axis=0) quotes['datetime'] = quotes['datetime'].astype(float) quotes['open'] = quotes['open'].astype(float) quotes['high'] = quotes['high'].astype(float) quotes['low'] = quotes['low'].astype(float) quotes['close'] = quotes['close'].astype(float) return quotes def check_price(datas): #移動平均線 s = pd.Series(datas['close']) sma25 = s.rolling(window=25).mean() #sma5 = s.rolling(window=5).mean() #plt.plot(datas['datetime'], sma5) deviation = 2 sigma = s.rolling(window=25).std(ddof=0) #σの計算 upper2_siguma = sma25 + sigma * deviation lower2_siguma = sma25 - sigma * deviation asset = ASSET have_position = 0 #ポジションを持っているか 0持っていない 1買を持っている 2売を持っている order_price = 0 cnt_win = 0 cnt_loose = 0 result = [] for idx, high in enumerate(datas['high']): low = datas['low'][idx] if have_position == 0: #ポジションを持っていない場合 if high >= upper2_siguma[idx]: #売り注文 print("売り注文 価格 2σ", high, upper2_siguma[idx]) have_position, order_price = open_order(have_position, order_price, 2, datas['high'][idx]) elif low <= lower2_siguma[idx]: #買い注文 print("買い注文 価格 2σ", low, lower2_siguma[idx]) have_position, order_price = open_order(have_position, order_price, 1, datas['low'][idx]) else: #print("価格チェック order_price price", order_price, datas['close'][idx]) #print("abs", abs(order_price - datas['close'][idx])) if abs(order_price - datas['close'][idx]) > DIFF_PRICE: print("order close", order_price, datas['close'][idx]) asset, have_position, order_price, cnt_win, cnt_loose = close_order(asset, have_position, order_price, cnt_win, cnt_loose, datas['close'][idx]) result.append(asset) return result, asset, cnt_win, cnt_loose def open_order(have_position, order_price, order_type, price): have_position = order_type order_price = price return have_position, order_price def close_order(asset, have_position, order_price, cnt_win, cnt_loose, price): if have_position == 1: #買いポジションの場合 diff_price = price - order_price elif have_position == 2: #売りポジションの場合 diff_price = order_price - price if diff_price > 0: #勝った場合 cnt_win += 1 else: cnt_loose += 1 asset = asset + diff_price * UNIT have_position = 0 return asset, have_position, order_price, cnt_win, cnt_loose if __name__ == '__main__': datas = get_datas() datas = convert_datas(datas) #print(datas.info()) result, asset, cnt_win, cnt_loose = check_price(datas) print("asset=", asset) print("cnt_win=", cnt_win) print("cnt_loose=", cnt_loose) additional_col = pd.DataFrame([result]).T additional_col.columns = ["asset"] additional_col.index = range(len(datas)) datas = pd.concat([datas, additional_col], axis=1) print(datas.info()) plt.grid() ax = plt.subplot() plt.plot(datas['datetime'], datas['asset']) plt.xlabel('Date') plt.ylabel('Price') plt.title('USD-JPY') plt.show() |
シミュレーションの結果は以下の通りになりました。
最終資産 540,200円
勝ち回数 764回
負け回数 627回
勝率 55%
勝ち回数 - 負け回数 = 137回
資産の推移グラフです。
また、5分足でもシミュレーションしてみました。
こちらは最終的な利益がさらに増えました。
最終資産 656,900円
勝ち回数 617回
負け回数 464回
勝率 57%
勝ち回数 - 負け回数 = 153回
資産の推移グラフです。
1分足で年間1,391回、5分足で年間1,081回のトレードを行っています。
今回の結果だけから見ると、1分足を5分足に変更しても
大きくトレード回数が増える訳ではなく(3割程度増加)
5分足と比べて負け回数が多くなっていますので、
最終的な結果も5分足の方がよくなっています。
移動平均線からの標準偏差の値をプロットしたものですが、
これを逆にして将来の為替は2次標準偏差以内に収まるだろうということが
55~57%ぐらいの確率で言えるのではないでしょうか。
勝率55~57%というのは派手に勝っているというイメージはありませんが、
年間1,000回以上の取引で、勝ち取引が150回近く上回っていますので
これを積み重ねて55万円の利益になるということですね。
今回は、2016年の1年間のデータのみで行ったので、
他の年の結果はどうなのか、
また利確も、損切も両方とも30pipsで行いましたが、
例えば損切だけ10pipsにした場合はどうなのかなども
シミュレーションしてみたいと思います。
また、今後はボリンジャーバンド以外のテクニカル指標にもトライしたいと思います。
スワップポイントとかスプレッド等を全く考慮できていないので、
早く証券会社のデモ口座でデモ取引やってみたいです。
本音を言うと、これほど単純なロジックで550%も
利益が積みあがるのかと信じられない思いです。
本当にプログラムが正しいかの検証も行っていきます。