In [1]:
import numpy as np
import pandas as pd

dataM1 = pd.read_csv('DAT_ASCII_EURUSD_M1_2015.csv', sep=';',
                     names=('Time','Open','High','Low','Close', ''),
                     index_col='Time', parse_dates=True)
dataM1.index += pd.offsets.Hour(7) #7時間のオフセット
In [2]:
# dfのデータからtfで指定するタイムフレームの4本足データを作成する関数
def TF_ohlc(df, tf):
    x = df.resample(tf).ohlc()
    O = x['Open']['open']
    H = x['High']['high']
    L = x['Low']['low']
    C = x['Close']['close']
    ret = pd.DataFrame({'Open': O, 'High': H, 'Low': L, 'Close': C},
                       columns=['Open','High','Low','Close'])
    return ret.dropna()

ohlc = TF_ohlc(dataM1, 'H') #1時間足データの作成
In [3]:
import indicators as ind #indicators.pyのインポート
FastMA = ind.iMA(ohlc, 8) #短期移動平均
SlowMA = ind.iMA(ohlc, 27) #長期移動平均
In [4]:
from pandas_highcharts.display import display_charts
df = pd.DataFrame({'Close': ohlc['Close'], 'FastMA': FastMA, 'SlowMA': SlowMA})
display_charts(df, chart_type="stock", title="MA cross", figsize=(640,480), grid=True)
In [5]:
#買いエントリーシグナル
BuyEntry = ((FastMA > SlowMA) & (FastMA.shift() <= SlowMA.shift())).values
#売りエントリーシグナル
SellEntry = ((FastMA < SlowMA) & (FastMA.shift() >= SlowMA.shift())).values
#買いエグジットシグナル
BuyExit = SellEntry.copy()
#売りエグジットシグナル
SellExit = BuyEntry.copy()
In [6]:
from numba import jit
#@jit
def Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit, lots=0.1, spread=2):
    Open = ohlc['Open'].values #始値
    Point = 0.0001 #1pipの値
    if(Open[0] > 50): Point = 0.01 #クロス円の1pipの値
    Spread = spread*Point #スプレッド
    Lots = lots*100000 #実際の売買量
    N = len(ohlc) #FXデータのサイズ
    BuyExit[N-2] = SellExit[N-2] = True #最後に強制エグジット
    BuyPrice = SellPrice = 0.0 # 売買価格
    
    LongTrade = np.zeros(N) # 買いトレード情報
    ShortTrade = np.zeros(N) # 売りトレード情報
    
    LongPL = np.zeros(N) # 買いポジションの損益
    ShortPL = np.zeros(N) # 売りポジションの損益

    for i in range(1,N):
        if BuyEntry[i-1] and BuyPrice == 0: #買いエントリーシグナル
            BuyPrice = Open[i]+Spread
            LongTrade[i] = BuyPrice #買いポジションオープン
        elif BuyExit[i-1] and BuyPrice != 0: #買いエグジットシグナル
            ClosePrice = Open[i]
            LongTrade[i] = -ClosePrice #買いポジションクローズ
            LongPL[i] = (ClosePrice-BuyPrice)*Lots #損益確定
            BuyPrice = 0

        if SellEntry[i-1] and SellPrice == 0: #売りエントリーシグナル
            SellPrice = Open[i]
            ShortTrade[i] = SellPrice #売りポジションオープン
        elif SellExit[i-1] and SellPrice != 0: #売りエグジットシグナル
            ClosePrice = Open[i]+Spread
            ShortTrade[i] = -ClosePrice #売りポジションクローズ
            ShortPL[i] = (SellPrice-ClosePrice)*Lots #損益確定
            SellPrice = 0

    return pd.DataFrame({'Long':LongTrade, 'Short':ShortTrade}, index=ohlc.index),\
            pd.DataFrame({'Long':LongPL, 'Short':ShortPL}, index=ohlc.index)
        
Trade, PL = Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit)        
In [7]:
def BacktestReport(Trade, PL):
    LongPL = PL['Long']
    LongTrades = np.count_nonzero(Trade['Long'])//2
    LongWinTrades = np.count_nonzero(LongPL.clip_lower(0))
    LongLoseTrades = np.count_nonzero(LongPL.clip_upper(0))
    print('買いトレード数 =', LongTrades)
    print('勝トレード数 =', LongWinTrades)
    print('最大勝トレード =', LongPL.max())
    print('平均勝トレード =', round(LongPL.clip_lower(0).sum()/LongWinTrades, 2))
    print('負トレード数 =', LongLoseTrades)
    print('最大負トレード =', LongPL.min())
    print('平均負トレード =', round(LongPL.clip_upper(0).sum()/LongLoseTrades, 2))
    print('勝率 =', round(LongWinTrades/LongTrades*100, 2), '%\n')

    ShortPL = PL['Short']
    ShortTrades = np.count_nonzero(Trade['Short'])//2
    ShortWinTrades = np.count_nonzero(ShortPL.clip_lower(0))
    ShortLoseTrades = np.count_nonzero(ShortPL.clip_upper(0))
    print('売りトレード数 =', ShortTrades)
    print('勝トレード数 =', ShortWinTrades)
    print('最大勝トレード =', ShortPL.max())
    print('平均勝トレード =', round(ShortPL.clip_lower(0).sum()/ShortWinTrades, 2))
    print('負トレード数 =', ShortLoseTrades)
    print('最大負トレード =', ShortPL.min())
    print('平均負トレード =', round(ShortPL.clip_upper(0).sum()/ShortLoseTrades, 2))
    print('勝率 =', round(ShortWinTrades/ShortTrades*100, 2), '%\n')

    Trades = LongTrades + ShortTrades
    WinTrades = LongWinTrades+ShortWinTrades
    LoseTrades = LongLoseTrades+ShortLoseTrades
    print('総トレード数 =', Trades)
    print('勝トレード数 =', WinTrades)
    print('最大勝トレード =', max(LongPL.max(), ShortPL.max()))
    print('平均勝トレード =', round((LongPL.clip_lower(0).sum()+ShortPL.clip_lower(0).sum())/WinTrades, 2))
    print('負トレード数 =', LoseTrades)
    print('最大負トレード =', min(LongPL.min(), ShortPL.min()))
    print('平均負トレード =', round((LongPL.clip_upper(0).sum()+ShortPL.clip_upper(0).sum())/LoseTrades, 2))
    print('勝率 =', round(WinTrades/Trades*100, 2), '%\n')

    GrossProfit = LongPL.clip_lower(0).sum()+ShortPL.clip_lower(0).sum()
    GrossLoss = LongPL.clip_upper(0).sum()+ShortPL.clip_upper(0).sum()
    Profit = GrossProfit+GrossLoss
    Equity = (LongPL+ShortPL).cumsum()
    MDD = (Equity.cummax()-Equity).max()
    print('総利益 =', round(GrossProfit, 2))
    print('総損失 =', round(GrossLoss, 2))
    print('総損益 =', round(Profit, 2))
    print('プロフィットファクター =', round(-GrossProfit/GrossLoss, 2))
    print('平均損益 =', round(Profit/Trades, 2))
    print('最大ドローダウン =', round(MDD, 2))
    print('リカバリーファクター =', round(Profit/MDD, 2))
    return Equity
Equity = BacktestReport(Trade, PL)
買いトレード数 = 132
勝トレード数 = 53
最大勝トレード = 488.0
平均勝トレード = 72.35
負トレード数 = 79
最大負トレード = -146.1
平均負トレード = -39.51
勝率 = 40.15 %

売りトレード数 = 132
勝トレード数 = 52
最大勝トレード = 320.7
平均勝トレード = 88.32
負トレード数 = 80
最大負トレード = -233.7
平均負トレード = -34.98
勝率 = 39.39 %

総トレード数 = 264
勝トレード数 = 105
最大勝トレード = 488.0
平均勝トレード = 80.26
負トレード数 = 159
最大負トレード = -233.7
平均負トレード = -37.23
勝率 = 39.77 %

総利益 = 8427.1
総損失 = -5920.0
総損益 = 2507.1
プロフィットファクター = 1.42
平均損益 = 9.5
最大ドローダウン = 485.1
リカバリーファクター = 5.17
In [8]:
Initial = 10000 # 初期資産
display_charts(pd.DataFrame({'Equity':Equity+Initial}), chart_type="stock", title="資産曲線", figsize=(640,480), grid=True)
In [9]:
def PositionLine(trade):
    PosPeriod = 0 #ポジションの期間
    Position = False #ポジションの有無
    Line = trade.copy()
    for i in range(len(Line)):
        if trade[i] > 0: Position = True 
        elif Position: PosPeriod += 1 # ポジションの期間をカウント
        if trade[i] < 0:
            if PosPeriod > 0:
                Line[i] = -trade[i]
                diff = (Line[i]-Line[i-PosPeriod])/PosPeriod
                for j in range(i-1, i-PosPeriod, -1):
                    Line[j] = Line[j+1]-diff # ポジションの期間を補間
                PosPeriod = 0
                Position = False
        if trade[i] == 0 and not Position: Line[i] = 'NaN'
    return Line

df = pd.DataFrame({'Open': ohlc['Open'],
                   'Long': PositionLine(Trade['Long'].values),
                   'Short': PositionLine(Trade['Short'].values)})
display_charts(df, chart_type="stock", title="トレードチャート", figsize=(640,480), grid=True)