前回は、「PythonでFXのヒストリカルデータを元にボリンジャーバンドを描写する」で
ヒストリカルデータを元にボリンジャーバンドを描写してみました。
で、テクニカル指標を描写しているだけでは面白くないので
テクニカル指標を使ってトレードを行うとどんな結果になるのか
シミュレーションを行ってみました。
手元にあるFXのヒストリカルデータを元にシミュレーションしますので、
注文処理も、利益確定・ストップロス処理も疑似的に実装します。
10万円の資金で始めて、1万通貨単位で建玉は一つだけ
(ポジションを持っている場合は注文をしない)
という条件でシミュレーションしてみました。
シミュレーションと言いつつ、非常に簡易的なものですので、
以下の点について注意が必要です。
注文を入れた金額で必ず約定している
取引手数料(スプレッドも)は一切考慮していない
スワップポイントは一切考慮していない
上記については、実際の取引では考慮する必要がありますので
今回のシミュレーションの結果は、実際の取引と異なると思われますが、
「テクニカル指標を使ってどのぐらい勝てそうか」という
傾向を見るために実施してみました。
今回使用したテクニカル指標はボリンジャーバンドのみです。
±2σに価格が近づくと、反発するだろうという性質を利用したものです。
データは1分足を使用しました。
ロジックとしては、現在価格が25分移動平均の±2σに到達した際に
買い・売り注文を入れ、約定価格から30pips移動したら
利確・損切をするというものです。
利確(予想通りに値動きした場合)でも、
損切(予想と逆に値動きした場合)でも
30pips値動きしたら注文を閉じます。
2016年1月1日00時00分から2017年1月1日00時00分のデータで実行しました。
まずはプログラムソースです。
(Python勉強中なので見苦しい点はご容赦ください)
# -*- 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%も
利益が積みあがるのかと信じられない思いです。
本当にプログラムが正しいかの検証も行っていきます。