Backtesting FX Systre with Python (1) It is a continuation of.
The only systems that can be backtested in (1) are those that market buy and sell at the opening price of the next bar for the signal generated from the fixed closing price. This time, we expanded from limit trading and open position profit / loss to profit taking and loss cut systems.
The overall flow is the same as (1). Since the common parts are briefly written, please refer to the article (1) for details.
Create EUR / USD, 1-hour 4-value data for 2015 as ʻohlc`.
import numpy as np
import pandas as pd
import indicators as ind #indicators.Import of py
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 hour offset
ohlc = ind.TF_ohlc(dataM1, 'H') #Creation of 1-hour data
Create a buy / sell signal for the system to be backtested. This time, the entry is the intersection of the moving average and the exit is the intersection of the moving average and the closing price.
The technical indicators used are two moving averages for entry and one moving average for exit.
FastMA = ind.iMA(ohlc, 10) #Short-term moving average for entry
SlowMA = ind.iMA(ohlc, 30) #Long-term moving average for entry
ExitMA = ind.iMA(ohlc, 5) #Moving average for exit
Create a buy / sell signal using these index values.
#Buy entry signal
BuyEntry = ((FastMA > SlowMA) & (FastMA.shift() <= SlowMA.shift())).values
#Sell entry signal
SellEntry = ((FastMA < SlowMA) & (FastMA.shift() >= SlowMA.shift())).values
#Buy exit signal
BuyExit = ((ohlc.Close < ExitMA) & (ohlc.Close.shift() >= ExitMA.shift())).values
#Sell exit signal
SellExit = ((ohlc.Close > ExitMA) & (ohlc.Close.shift() <= ExitMA.shift())).values
These signals are generated at all positions that meet the conditions of intersection, but some are not actually adopted depending on the presence or absence of positions. The control around that is done by the following backtest function.
This time, the following two points have been added as extensions of the trading rules of the system.
Therefore, I increased the argument of the function as follows.
def Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit, lots=0.1, spread=2, TP=0, SL=0, Limit=0, Expiration=10):
Substituting a positive value for Limit
in this will indicate a limit price that is Limit
pips cheaper for buying and a limit price that is Limit
pips higher for selling, without buying or selling immediately at the entry signal. .. ʻExpiration` is the validity period of the limit price and is specified by the number of bars.
TP
and SL
represent profit (pips) for profit taking and loss (pips) for loss cut, respectively. It works by substituting a positive value.
The whole function looks like this:
def Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit, lots=0.1, spread=2, TP=0, SL=0, Limit=0, Expiration=10):
Open = ohlc.Open.values #Open price
Low = ohlc.Low.values #Low price
High = ohlc.High.values #High price
Point = 0.0001 #Value of 1pip
if(Open[0] > 50): Point = 0.01 #1 pip value of cross circle
Spread = spread*Point #Spread
Lots = lots*100000 #Actual trading volume
N = len(ohlc) #FX data size
LongTrade = np.zeros(N) #Buy trade information
ShortTrade = np.zeros(N) #Sell trade information
#Buy entry price
BuyEntryS = np.hstack((False, BuyEntry[:-1])) #Buy entry signal shift
if Limit == 0: LongTrade[BuyEntryS] = Open[BuyEntryS]+Spread #Market buying
else: #Limit buy
for i in range(N-Expiration):
if BuyEntryS[i]:
BuyLimit = Open[i]-Limit*Point #Limit price
for j in range(Expiration):
if Low[i+j] <= BuyLimit: #Contract conditions
LongTrade[i+j] = BuyLimit+Spread
break
#Buy exit price
BuyExitS = np.hstack((False, BuyExit[:-2], True)) #Buy exit signal shift
LongTrade[BuyExitS] = -Open[BuyExitS]
#Sell entry price
SellEntryS = np.hstack((False, SellEntry[:-1])) #Sell entry signal shift
if Limit == 0: ShortTrade[SellEntryS] = Open[SellEntryS] #Market sale
else: #Limit selling
for i in range(N-Expiration):
if SellEntryS[i]:
SellLimit = Open[i]+Limit*Point #Limit price
for j in range(Expiration):
if High[i+j] >= SellLimit: #Contract conditions
ShortTrade[i+j] = SellLimit
break
#Sell exit price
SellExitS = np.hstack((False, SellExit[:-2], True)) #Sell exit signal shift
ShortTrade[SellExitS] = -(Open[SellExitS]+Spread)
LongPL = np.zeros(N) #Profit and Loss of Buy Position
ShortPL = np.zeros(N) #Sell position profit or loss
BuyPrice = SellPrice = 0.0 #Selling price
for i in range(1,N):
if LongTrade[i] > 0: #Buy entry signal
if BuyPrice == 0:
BuyPrice = LongTrade[i]
ShortTrade[i] = -BuyPrice #Sell exit
else: LongTrade[i] = 0
if ShortTrade[i] > 0: #Sell entry signal
if SellPrice == 0:
SellPrice = ShortTrade[i]
LongTrade[i] = -SellPrice #Buy exit
else: ShortTrade[i] = 0
if LongTrade[i] < 0: #Buy exit signal
if BuyPrice != 0:
LongPL[i] = -(BuyPrice+LongTrade[i])*Lots #Profit and loss settlement
BuyPrice = 0
else: LongTrade[i] = 0
if ShortTrade[i] < 0: #Sell exit signal
if SellPrice != 0:
ShortPL[i] = (SellPrice+ShortTrade[i])*Lots #Profit and loss settlement
SellPrice = 0
else: ShortTrade[i] = 0
if BuyPrice != 0 and SL > 0: #Settlement of buy position by SL
StopPrice = BuyPrice-SL*Point
if Low[i] <= StopPrice:
LongTrade[i] = -StopPrice
LongPL[i] = -(BuyPrice+LongTrade[i])*Lots #Profit and loss settlement
BuyPrice = 0
if BuyPrice != 0 and TP > 0: #Settlement of buy position by TP
LimitPrice = BuyPrice+TP*Point
if High[i] >= LimitPrice:
LongTrade[i] = -LimitPrice
LongPL[i] = -(BuyPrice+LongTrade[i])*Lots #Profit and loss settlement
BuyPrice = 0
if SellPrice != 0 and SL > 0: #Settlement of sell position by SL
StopPrice = SellPrice+SL*Point
if High[i]+Spread >= StopPrice:
ShortTrade[i] = -StopPrice
ShortPL[i] = (SellPrice+ShortTrade[i])*Lots #Profit and loss settlement
SellPrice = 0
if SellPrice != 0 and TP > 0: #Settlement of sell position by TP
LimitPrice = SellPrice-TP*Point
if Low[i]+Spread <= LimitPrice:
ShortTrade[i] = -LimitPrice
ShortPL[i] = (SellPrice+ShortTrade[i])*Lots #Profit and loss settlement
SellPrice = 0
return pd.DataFrame({'Long':LongTrade, 'Short':ShortTrade}, index=ohlc.index),\
pd.DataFrame({'Long':LongPL, 'Short':ShortPL}, index=ohlc.index)
It's been quite a long time, but to explain it briefly, first, preprocess the signal as follows.
BuyEntry
, SellEntry
, BuyExit
, SellExit
by one sample to make BuyEntryS
, SellEntryS
, BuyExitS
, SellExitS
. This is so that the signal will appear in the bar where the actual sale is made.LongTrade
, ShortTrade
for the price to be bought or sold at the time of the signal. The entry is a positive number, the exit is a negative number, and in the case of limit trading, the trading price is entered at the position of the bar that first reached the limit price during the valid period.After that, the bar is turned from the beginning to the end, and trading and settlement processing is performed while checking the existence of positions and the profit and loss of positions. Returns the actual traded price to LongTrade
, ShortTrade
and the fixed profit or loss to LongPL
, ShortPL
.
The following example is a backtest of a system that buys and sells at a limit price 20 pips away from the entry signal price and settles with a profit of 100 pips or a loss of 50 pips separately from the exit signal.
Trade, PL = Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit, TP=100, SL=50, Limit=20)
This time, I will display only the asset curve.
from pandas_highcharts.display import display_charts
Initial = 10000 #Initial assets
Equity = (PL.Long+PL.Short).cumsum() #Cumulative profit and loss
display_charts(pd.DataFrame({'Equity':Equity+Initial}), chart_type="stock", title="Asset curve", figsize=(640,480), grid=True)
The code posted in this article has been uploaded below. MT5IndicatorsPy/EA_sample2.ipynb
Recommended Posts