You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: doc/examples/Multiple Time Frames.py
+14-12Lines changed: 14 additions & 12 deletions
Original file line number
Diff line number
Diff line change
@@ -3,15 +3,16 @@
3
3
# jupytext:
4
4
# text_representation:
5
5
# extension: .py
6
-
# format_name: light
7
-
# format_version: '1.5'
8
-
# jupytext_version: 1.5.1
6
+
# format_name: percent
7
+
# format_version: '1.3'
8
+
# jupytext_version: 1.17.1
9
9
# kernelspec:
10
10
# display_name: Python 3
11
11
# language: python
12
12
# name: python3
13
13
# ---
14
14
15
+
# %% [markdown]
15
16
# Multiple Time Frames
16
17
# ============
17
18
#
@@ -32,7 +33,7 @@
32
33
# [Tulipy](https://tulipindicators.org),
33
34
# but among us, let's introduce the two indicators we'll be using.
34
35
35
-
# +
36
+
# %%
36
37
importpandasaspd
37
38
38
39
@@ -52,8 +53,7 @@ def RSI(array, n):
52
53
return100-100/ (1+rs)
53
54
54
55
55
-
# -
56
-
56
+
# %% [markdown]
57
57
# The strategy roughly goes like this:
58
58
#
59
59
# Buy a position when:
@@ -66,7 +66,7 @@ def RSI(array, n):
66
66
#
67
67
# We need to provide bars data in the _lowest time frame_ (i.e. daily) and resample it to any higher time frame (i.e. weekly) that our strategy requires.
68
68
69
-
# +
69
+
# %%
70
70
frombacktestingimportStrategy, Backtest
71
71
frombacktesting.libimportresample_apply
72
72
@@ -112,34 +112,36 @@ def next(self):
112
112
# close the position, if any.
113
113
elifprice<.98*self.ma10[-1]:
114
114
self.position.close()
115
-
# -
116
115
116
+
# %% [markdown]
117
117
# Let's see how our strategy fares replayed on nine years of Google stock data.
118
118
119
-
# +
119
+
# %%
120
120
frombacktesting.testimportGOOG
121
121
122
122
backtest=Backtest(GOOG, System, commission=.002)
123
123
backtest.run()
124
-
# -
125
124
125
+
# %% [markdown]
126
126
# Meager four trades in the span of nine years and with zero return? How about if we optimize the parameters a bit?
127
127
128
-
# +
128
+
# %%
129
129
# %%time
130
130
131
131
backtest.optimize(d_rsi=range(10, 35, 5),
132
132
w_rsi=range(10, 35, 5),
133
133
level=range(30, 80, 10))
134
-
# -
135
134
135
+
# %%
136
136
backtest.plot()
137
137
138
+
# %% [markdown]
138
139
# Better. While the strategy doesn't perform as well as simple buy & hold, it does so with significantly lower exposure (time in market).
139
140
#
140
141
# In conclusion, to test strategies on multiple time frames, you need to pass in OHLC data in the lowest time frame, then resample it to higher time frames, apply the indicators, then resample back to the lower time frame, filling in the in-betweens.
141
142
# Which is what the function [`backtesting.lib.resample_apply()`](https://kernc.github.io/backtesting.py/doc/backtesting/lib.html#backtesting.lib.resample_apply) does for you.
# is an exhaustive search through a set of specified sets of values of hyperparameters. One evaluates the performance for each set of parameters and finally selects the combination that performs best.
93
95
#
94
96
# Let's optimize our strategy on Google stock data using _randomized_ grid search over the parameter space, evaluating at most (approximately) 200 randomly chosen combinations:
95
97
96
-
# +
98
+
# %%
97
99
# %%time
98
100
99
101
frombacktestingimportBacktest
@@ -112,32 +114,38 @@ def next(self):
112
114
max_tries=200,
113
115
random_state=0,
114
116
return_heatmap=True)
115
-
# -
116
117
118
+
# %% [markdown]
117
119
# Notice `return_heatmap=True` parameter passed to
# It makes the function return a heatmap series along with the usual stats of the best run.
120
122
# `heatmap` is a pandas Series indexed with a MultiIndex, a cartesian product of all permissible (tried) parameter values.
121
123
# The series values are from the `maximize=` argument we provided.
122
124
125
+
# %%
123
126
heatmap
124
127
128
+
# %% [markdown]
125
129
# This heatmap contains the results of all the runs,
126
130
# making it very easy to obtain parameter combinations for e.g. three best runs:
127
131
132
+
# %%
128
133
heatmap.sort_values().iloc[-3:]
129
134
135
+
# %% [markdown]
130
136
# But we use vision to make judgements on larger data sets much faster.
131
137
# Let's plot the whole heatmap by projecting it on two chosen dimensions.
132
138
# Say we're mostly interested in how parameters `n1` and `n2`, on average, affect the outcome.
133
139
140
+
# %%
134
141
hm=heatmap.groupby(['n1', 'n2']).mean().unstack()
135
142
hm=hm[::-1]
136
143
hm
137
144
145
+
# %% [markdown]
138
146
# Let's plot this table as a heatmap:
139
147
140
-
# +
148
+
# %%
141
149
# %matplotlib inline
142
150
143
151
importmatplotlib.pyplotasplt
@@ -151,8 +159,8 @@ def next(self):
151
159
ax.set_ylabel('n1'),
152
160
ax.figure.colorbar(im, ax=ax),
153
161
)
154
-
# -
155
162
163
+
# %% [markdown]
156
164
# We see that, on average, we obtain the highest result using trend-determining parameters `n1=30` and `n2=100` or `n1=70` and `n2=80`,
157
165
# and it's not like other nearby combinations work similarly well — for our particular strategy, these combinations really stand out.
158
166
#
@@ -163,13 +171,13 @@ def next(self):
163
171
#
164
172
# <a id=plot-heatmaps></a>
165
173
166
-
# +
174
+
# %%
167
175
frombacktesting.libimportplot_heatmaps
168
176
169
177
170
178
plot_heatmaps(heatmap, agg='mean')
171
-
# -
172
179
180
+
# %% [markdown]
173
181
# ## Model-based optimization
174
182
#
175
183
# Above, we used _randomized grid search_ optimization method. Any kind of grid search, however, might be computationally expensive for large data sets. In the follwing example, we will use
@@ -179,12 +187,12 @@ def next(self):
179
187
#
180
188
# So, with `method="sambo"`:
181
189
182
-
# +
190
+
# %%
183
191
# %%capture
184
192
185
193
# ! pip install sambo # This is a run-time dependency
# Notice how the optimization runs somewhat slower even though `max_tries=` is lower. This is due to the sequential nature of the algorithm and should actually perform quite comparably even in cases of _much larger parameter spaces_ where grid search would effectively blow up, likely reaching a better optimum than a simple randomized search would.
207
216
# A note of warning, again, to take steps to avoid
0 commit comments