-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtbbacktesterrm.py
138 lines (131 loc) · 6.18 KB
/
tbbacktesterrm.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
#
# Event-Based Backtesting
# --Trading Bot Backtester
# (incl. Risk Management)
#
# (c) Dr. Yves J. Hilpisch
#
import numpy as np
import pandas as pd
import backtestingrm as btr
class TBBacktesterRM(btr.BacktestingBaseRM):
def _reshape(self, state):
''' Helper method to reshape state objects.
'''
return np.reshape(state, [1, self.env.lags, self.env.n_features])
def backtest_strategy(self, sl=None, tsl=None, tp=None,
wait=5, guarantee=False):
''' Event-based backtesting of the trading bot's performance.
Incl. stop loss, trailing stop loss and take profit.
'''
self.units = 0
self.position = 0
self.trades = 0
self.sl = sl
self.tsl = tsl
self.tp = tp
self.wait = 0
self.current_balance = self.initial_amount
self.net_wealths = list()
for bar in range(self.env.lags, len(self.env.data)):
self.wait = max(0, self.wait - 1)
date, price = self.get_date_price(bar)
if self.trades == 0:
print(50 * '=')
print(f'{date} | *** START BACKTEST ***')
self.print_balance(bar)
print(50 * '=')
# stop loss order
if sl is not None and self.position != 0:
rc = (price - self.entry_price) / self.entry_price
if self.position == 1 and rc < -self.sl:
print(50 * '-')
if guarantee:
price = self.entry_price * (1 - self.sl)
print(f'*** STOP LOSS (LONG | {-self.sl:.4f}) ***')
else:
print(f'*** STOP LOSS (LONG | {rc:.4f}) ***')
self.place_sell_order(bar, units=self.units, gprice=price)
self.wait = wait
self.position = 0
elif self.position == -1 and rc > self.sl:
print(50 * '-')
if guarantee:
price = self.entry_price * (1 + self.sl)
print(f'*** STOP LOSS (SHORT | -{self.sl:.4f}) ***')
else:
print(f'*** STOP LOSS (SHORT | -{rc:.4f}) ***')
self.place_buy_order(bar, units=-self.units, gprice=price)
self.wait = wait
self.position = 0
# trailing stop loss order
if tsl is not None and self.position != 0:
self.max_price = max(self.max_price, price)
self.min_price = min(self.min_price, price)
rc_1 = (price - self.max_price) / self.entry_price
rc_2 = (self.min_price - price) / self.entry_price
if self.position == 1 and rc_1 < -self.tsl:
print(50 * '-')
print(f'*** TRAILING SL (LONG | {rc_1:.4f}) ***')
self.place_sell_order(bar, units=self.units)
self.wait = wait
self.position = 0
elif self.position == -1 and rc_2 < -self.tsl:
print(50 * '-')
print(f'*** TRAILING SL (SHORT | {rc_2:.4f}) ***')
self.place_buy_order(bar, units=-self.units)
self.wait = wait
self.position = 0
# take profit order
if tp is not None and self.position != 0:
rc = (price - self.entry_price) / self.entry_price
if self.position == 1 and rc > self.tp:
print(50 * '-')
if guarantee:
price = self.entry_price * (1 + self.tp)
print(f'*** TAKE PROFIT (LONG | {self.tp:.4f}) ***')
else:
print(f'*** TAKE PROFIT (LONG | {rc:.4f}) ***')
self.place_sell_order(bar, units=self.units, gprice=price)
self.wait = wait
self.position = 0
elif self.position == -1 and rc < -self.tp:
print(50 * '-')
if guarantee:
price = self.entry_price * (1 - self.tp)
print(f'*** TAKE PROFIT (SHORT | {self.tp:.4f}) ***')
else:
print(f'*** TAKE PROFIT (SHORT | {-rc:.4f}) ***')
self.place_buy_order(bar, units=-self.units, gprice=price)
self.wait = wait
self.position = 0
state = self.env.get_state(bar)
action = np.argmax(self.model.predict(
self._reshape(state.values))[0, 0])
position = 1 if action == 1 else -1
if self.position in [0, -1] and position == 1 and self.wait == 0:
if self.verbose:
print(50 * '-')
print(f'{date} | *** GOING LONG ***')
if self.position == -1:
self.place_buy_order(bar - 1, units=-self.units)
self.place_buy_order(bar - 1, amount=self.current_balance)
if self.verbose:
self.print_net_wealth(bar)
self.position = 1
elif self.position in [0, 1] and position == -1 and self.wait == 0:
if self.verbose:
print(50 * '-')
print(f'{date} | *** GOING SHORT ***')
if self.position == 1:
self.place_sell_order(bar - 1, units=self.units)
self.place_sell_order(bar - 1, amount=self.current_balance)
if self.verbose:
self.print_net_wealth(bar)
self.position = -1
self.net_wealths.append((date, self.calculate_net_wealth(price)))
self.net_wealths = pd.DataFrame(self.net_wealths,
columns=['date', 'net_wealth'])
self.net_wealths.set_index('date', inplace=True)
self.net_wealths.index = pd.DatetimeIndex(self.net_wealths.index)
self.close_out(bar)