forked from cuemacro/finmarketpy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
backtest_example.py
210 lines (162 loc) · 7.43 KB
/
backtest_example.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
__author__ = 'saeedamen' # Saeed Amen
#
# Copyright 2016-2020 Cuemacro - https://www.cuemacro.com / @cuemacro
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at http:https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
# See the License for the specific language governing permissions and limitations under the License.
#
"""
Gives several examples of backtesting simple trading strategies, using Backtest (a lower level class)
"""
from findatapy.timeseries import Calculations
# choose run_example = 0 for everything
# run_example = 1 - do a backtest of a FX basket with trend following
# run_example = 2 - do a backtest of EURUSD traded with trend following
run_example = 0
###### backtest simple trend following strategy for FX spot basket
if run_example == 1 or run_example == 0:
# For backtest and loading data
from finmarketpy.backtest import BacktestRequest, Backtest
from findatapy.market import Market, MarketDataRequest, MarketDataGenerator
from findatapy.util.fxconv import FXConv
# For logging
from findatapy.util.loggermanager import LoggerManager
# For signal generation
from finmarketpy.economics import TechIndicator, TechParams
# For plotting
from chartpy import Chart, Style
logger = LoggerManager().getLogger(__name__)
import datetime
backtest = Backtest()
br = BacktestRequest()
fxconv = FXConv()
# Get all asset data
br.start_date = "02 Jan 1990"
br.finish_date = datetime.datetime.utcnow()
br.spot_tc_bp = 2.5 # 2.5 bps bid/ask spread
br.ann_factor = 252
# Have vol target for each signal
br.signal_vol_adjust = True
br.signal_vol_target = 0.05
br.signal_vol_max_leverage = 3
br.signal_vol_periods = 60
br.signal_vol_obs_in_year = 252
br.signal_vol_rebalance_freq = 'BM'
br.signal_vol_resample_freq = None
tech_params = TechParams();
tech_params.sma_period = 200;
indicator = 'SMA'
# Pick USD crosses in G10 FX
# Note: we are calculating returns from spot (it is much better to use to total return
# indices for FX, which include carry)
logger.info("Loading asset data...")
tickers = ['EURUSD', 'USDJPY', 'GBPUSD', 'AUDUSD', 'USDCAD',
'NZDUSD', 'USDCHF', 'USDNOK', 'USDSEK']
vendor_tickers = ['FRED/DEXUSEU', 'FRED/DEXJPUS', 'FRED/DEXUSUK', 'FRED/DEXUSAL', 'FRED/DEXCAUS',
'FRED/DEXUSNZ', 'FRED/DEXSZUS', 'FRED/DEXNOUS', 'FRED/DEXSDUS']
md_request = MarketDataRequest(
start_date="01 Jan 1989", # start date
finish_date=datetime.date.today(), # finish date
freq='daily', # daily data
data_source='quandl', # use Quandl as data source
tickers=tickers, # ticker (findatapy)
fields=['close'], # which fields to download
vendor_tickers=vendor_tickers, # ticker (Quandl)
vendor_fields=['close'], # which Bloomberg fields to download
cache_algo='internet_load_return') # how to return data
market = Market(market_data_generator=MarketDataGenerator())
asset_df = market.fetch_market(md_request)
spot_df = asset_df
logger.info("Running backtest...")
# Use technical indicator to create signals
# (we could obviously create whatever function we wanted for generating the signal dataframe)
tech_ind = TechIndicator()
tech_ind.create_tech_ind(spot_df, indicator, tech_params);
signal_df = tech_ind.get_signal()
contract_value_df = None
# Use the same data for generating signals
backtest.calculate_trading_PnL(br, asset_df, signal_df, contract_value_df, run_in_parallel=False)
port = backtest.portfolio_cum()
port.columns = [indicator + ' = ' + str(tech_params.sma_period) + ' ' + str(backtest.portfolio_pnl_desc()[0])]
signals = backtest.portfolio_signal()
# Print the last positions (we could also save as CSV etc.)
print(signals.tail(1))
style = Style()
style.title = "FX trend strategy"
style.source = 'Quandl'
style.scale_factor = 1
style.file_output = 'fx-trend-example.png'
Chart().plot(port, style=style)
###### backtest simple trend following strategy for FX spot basket
if run_example == 2 or run_example == 0:
# For backtest and loading data
from finmarketpy.backtest import Backtest, BacktestRequest
from findatapy.market import Market, MarketDataRequest, MarketDataGenerator
from findatapy.util.fxconv import FXConv
from findatapy.timeseries import Calculations
# For logging
from findatapy.util import LoggerManager
# For signal generation
from finmarketpy.economics import TechIndicator, TechParams
# For plotting
from chartpy import Chart, Style
logger = LoggerManager().getLogger(__name__)
import datetime
backtest = Backtest()
br = BacktestRequest()
fxconv = FXConv()
# Get all asset data
br.start_date = "02 Jan 1990"
br.finish_date = datetime.datetime.utcnow()
br.spot_tc_bp = 2.5 # 2.5 bps bid/ask spread
br.ann_factor = 252
tech_params = TechParams();
tech_params.sma_period = 200;
indicator = 'SMA'
tech_params.only_allow_longs = True
# tech_params.only_allow_shorts = True
# Pick EUR/USD
# Note: we are calculating returns from spot (it is much better to use to total return
# indices for FX, which include carry)
logger.info("Loading asset data...")
md_request = MarketDataRequest(
start_date="01 Jan 1989", # start date
finish_date=datetime.date.today(), # finish date
freq='daily', # daily data
data_source='quandl', # use Quandl as data source
tickers=['EURUSD'], # ticker (findatapy)
fields=['close'], # which fields to download
vendor_tickers=['FRED/DEXUSEU'], # ticker (Quandl)
vendor_fields=['close'], # which Bloomberg fields to download
cache_algo='internet_load_return') # how to return data
market = Market(market_data_generator=MarketDataGenerator())
asset_df = market.fetch_market(md_request)
spot_df = asset_df
logger.info("Running backtest...")
# Use technical indicator to create signals
# (we could obviously create whatever function we wanted for generating the signal dataframe)
tech_ind = TechIndicator()
tech_ind.create_tech_ind(spot_df, indicator, tech_params);
signal_df = tech_ind.get_signal()
# Use the same data for generating signals
backtest.calculate_trading_PnL(br, asset_df, signal_df, contract_value_df=None, run_in_parallel=False)
port = backtest.portfolio_cum()
port.columns = [indicator + ' = ' + str(tech_params.sma_period) + ' ' + str(backtest.portfolio_pnl_desc()[0])]
signals = backtest.portfolio_signal() # get final signals for each series
returns = backtest.pnl() # get P&L for each series
calculations = Calculations()
trade_returns = calculations.calculate_individual_trade_gains(signals, returns)
print(trade_returns)
# Print the last positions (we could also save as CSV etc.)
print(signals.tail(1))
style = Style()
style.title = "EUR/USD trend model"
style.source = 'Quandl'
style.scale_factor = 1
style.file_output = 'eurusd-trend-example.png'
Chart(port, style=style).plot()