FX Python システムトレード

FXで1年間550%の利益?PythonでFXシストレをシミュレーションした結果

スポンサードリンク

前回は、「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%も
利益が積みあがるのかと信じられない思いです。
本当にプログラムが正しいかの検証も行っていきます。

スポンサードリンク

-FX, Python, システムトレード
-, ,

© 2024 FX・ビットコイン・オプションのシステムトレード開発と取引録 Powered by AFFINGER5