-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
London Breakout backtest.py
286 lines (220 loc) · 10.4 KB
/
London Breakout backtest.py
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# coding: utf-8
# In[1]:
#this is to London, the greatest city in the world
#i was a Londoner, proud of being Londoner, how i love the city!
#to St Paul, Tate Modern, Millennium Bridge and so much more!
#okay, lets get down to business
#the idea of london break out strategy is to take advantage of fx trading hour
#basically fx trading is 24 hour non stop for weekdays
#u got tokyo, before tokyo closes, u got london
#in the afternoon, u got new york, when new york closes, its sydney
#and several hours later, tokyo starts again
#however, among these three major players
#london is where the majority trades are executed
#not sure if it will stay the same after brexit actually takes place
#what we intend to do is look at the last trading hour before london starts
#we set up our thresholds based on that hours high and low
#when london market opens, we examine the first 30 minutes
#if it goes way above or below thresholds
#we long or short certain currency pairs
#and we clear our positions based on target and stop loss we set
#if they havent reach the trigger condition by the end of trading hour
#we exit our trades and close all positions
#it sounds easy in practise
#just a simple prediction of london fx market based on tokyo market
#but the code of london breakout is extremely time consuming
#first, we need to get one minute frequency dataset for backtest
#i would recommend this website
# http://www.histdata.com/download-free-forex-data/?/excel/1-minute-bar-quotes
#we can get as many as free datasets of various currency pairs we want
#before our backtesting, we should cleanse the raw data
#what we get from the website is one minute frequency bid-ask price
#i take the average of em and add a header called price
#i save it on local disk then read it via python
#please note that this website uses new york time zone utc -5
#for non summer daylight saving time
#london market starts at gmt 8 am
#which is est 3 am
#daylight saving time is another story
#what a stupid idea it is
import os
os.chdir('d:/')
import matplotlib.pyplot as plt
import pandas as pd
# In[2]:
def london_breakout(df):
df['signals']=0
#cumsum is the cumulated sum of signals
#later we would use it to control our positions
df['cumsum']=0
#upper and lower are our thresholds
df['upper']=0.0
df['lower']=0.0
return df
def signal_generation(df,method):
#tokyo_price is a list to store average price of
#the last trading hour of tokyo market
#we use max, min to define the real threshold later
tokyo_price=[]
#risky_stop is a parameter set by us
#it is to reduce the risk exposure to volatility
#i am using 100 basis points
#for instance, we have defined our upper and lower thresholds
#however, when london market opens
#the price goes skyrocketing
#say 200 basis points above upper threshold
#i personally wouldnt get in the market as its too risky
#also, my stop loss and target is 50 basis points
#just half of my risk interval
#i will use this variable for later stop loss set up
risky_stop=0.01
#this is another parameter set by us
#it is about how long opening volatility would wear off
#for me, 30 minutes after the market opening is the boundary
#as long as its under 30 minutes after the market opening
#if the price reaches threshold level, i will trade on signals
open_minutes=30
#this is the price when we execute a trade
#we need to save it to set up the stop loss
executed_price=float(0)
signals=method(df)
signals['date']=pd.to_datetime(signals['date'])
#this is the core part
#the time complexity for this part is extremely high
#as there are too many constraints
#if u have a better idea to optimize it
#plz let me know
for i in range(len(signals)):
#as mentioned before
#the dataset use eastern standard time
#so est 2am is the last hour before london starts
#we try to append all the price into the list called threshold
if signals['date'][i].hour==2:
tokyo_price.append(signals['price'][i])
#est 3am which is gmt 8am
#thats when london market starts
#good morning city of london and canary wharf!
#right at this moment
#we get max and min of the price of tokyo trading hour
#we set up the threshold as the way it is
#alternatively, we can put 10 basis points above and below thresholds
#we also use upper and lower list to keep track of our thresholds
#and now we clear the list called threshold
elif signals['date'][i].hour==3 and signals['date'][i].minute==0:
upper=max(tokyo_price)
lower=min(tokyo_price)
signals.at[i,'upper']=upper
signals.at[i,'lower']=lower
tokyo_price=[]
#prior to 30 minutes i have mentioned before
#as long as its under 30 minutes after market opening
#signals will be generated once conditions are met
#this is a relatively risky way
#alternatively, we can set the signal generation time at a fixed point
#when its gmt 8 30 am, we check the conditions to see if there is any signal
elif signals['date'][i].hour==3 and signals['date'][i].minute<open_minutes:
#again, we wanna keep track of thresholds during signal generation periods
signals.at[i,'upper']=upper
signals.at[i,'lower']=lower
#this is the condition of signals generation
#when the price is above upper threshold
#we set signals to 1 which implies long
if signals['price'][i]-upper>0:
signals.at[i,'signals']=1
#we use cumsum to check the cumulated sum of signals
#we wanna make sure that
#only the first price above upper threshold triggers the signal
#also, if it goes skyrocketing
#say 200 basis points above, which is 100 above our risk tolerance
#we set it as a false alarm
signals['cumsum']=signals['signals'].cumsum()
if signals['price'][i]-upper>risky_stop:
signals.at[i,'signals']=0
elif signals['cumsum'][i]>1:
signals.at[i,'signals']=0
else:
#we also need to store the price when we execute a trade
#its for stop loss calculation
executed_price=signals['price'][i]
#vice versa
if signals['price'][i]-lower<0:
signals.at[i,'signals']=-1
signals['cumsum']=signals['signals'].cumsum()
if lower-signals['price'][i]>risky_stop:
signals.at[i,'signals']=0
elif signals['cumsum'][i]<-1:
signals.at[i,'signals']=0
else:
executed_price=signals['price'][i]
#when its gmt 5 pm, london market closes
#we use cumsum to see if there is any position left open
#we take -cumsum as a signal
#if there is no open position, -0 is still 0
elif signals['date'][i].hour==12:
signals['cumsum']=signals['signals'].cumsum()
signals.at[i,'signals']=-signals['cumsum'][i]
#during london trading hour after opening but before closing
#we still use cumsum to check our open positions
#if there is any open position
#we set our condition at original executed price +/- half of the risk interval
#when it goes above or below our risk tolerance
#we clear positions to claim profit or loss
else:
signals['cumsum']=signals['signals'].cumsum()
if signals['cumsum'][i]!=0:
if signals['price'][i]>executed_price+risky_stop/2:
signals.at[i,'signals']=-signals['cumsum'][i]
if signals['price'][i]<executed_price-risky_stop/2:
signals.at[i,'signals']=-signals['cumsum'][i]
return signals
def plot(new):
#the first plot is price with LONG/SHORT positions
fig=plt.figure()
ax=fig.add_subplot(111)
new['price'].plot(label='price')
ax.plot(new.loc[new['signals']==1].index,new['price'][new['signals']==1],lw=0,marker='^',c='g',label='LONG')
ax.plot(new.loc[new['signals']==-1].index,new['price'][new['signals']==-1],lw=0,marker='v',c='r',label='SHORT')
#this is the part where i add some vertical line to indicate market beginning and ending
date=new.index[0].strftime('%Y-%m-%d')
plt.axvline('%s 03:00:00'%(date),linestyle=':',c='k')
plt.axvline('%s 12:00:00'%(date),linestyle=':',c='k')
plt.legend(loc='best')
plt.title('London Breakout')
plt.ylabel('price')
plt.xlabel('Date')
plt.grid(True)
plt.show()
#lets look at the market opening and break it down into 110 minutes
#we wanna observe how the price goes beyond the threshold
f='%s 02:50:00'%(date)
l='%s 03:30:00'%(date)
news=signals[f:l]
fig=plt.figure()
bx=fig.add_subplot(111)
bx.plot(news.loc[news['signals']==1].index,news['price'][news['signals']==1],lw=0,marker='^',markersize=10,c='g',label='LONG')
bx.plot(news.loc[news['signals']==-1].index,news['price'][news['signals']==-1],lw=0,marker='v',markersize=10,c='r',label='SHORT')
#i only need to plot non zero thresholds
#zero is the value outta market opening period
bx.plot(news.loc[news['upper']!=0].index,news['upper'][news['upper']!=0],lw=0,marker='.',markersize=7,c='#BC8F8F',label='upper threshold')
bx.plot(news.loc[news['lower']!=0].index,news['lower'][news['lower']!=0],lw=0,marker='.',markersize=5,c='#FF4500',label='lower threshold')
bx.plot(news['price'],label='price')
plt.grid(True)
plt.ylabel('price')
plt.xlabel('time interval')
plt.xticks([])
plt.title('%s Market Opening'%date)
plt.legend(loc='best')
plt.show()
# In[3]:
def main():
df=pd.read_csv('gbpusd.csv')
signals=signal_generation(df,london_breakout)
new=signals
new.set_index(pd.to_datetime(signals['date']),inplace=True)
date=new.index[0].strftime('%Y-%m-%d')
new=new['%s'%date]
plot(new)
#how to calculate stats could be found from my other code called Heikin-Ashi
# https://github.com/je-suis-tm/quant-trading/blob/master/heikin%20ashi%20backtest.py
if __name__ == '__main__':
main()