-
Notifications
You must be signed in to change notification settings - Fork 107
/
backtest.py
120 lines (93 loc) · 4.5 KB
/
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
import config
import backtrader, pandas, sqlite3
from datetime import date, datetime, time, timedelta
class OpeningRangeBreakout(backtrader.Strategy):
params = dict(
num_opening_bars=15
)
def __init__(self):
self.opening_range_low = 0
self.opening_range_high = 0
self.opening_range = 0
self.bought_today = False
self.order = None
def log(self, txt, dt=None):
if dt is None:
dt = self.datas[0].datetime.datetime()
print('%s, %s' % (dt, txt))
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
return
# Check if an order has been completed
if order.status in [order.Completed]:
order_details = f"{order.executed.price}, Cost: {order.executed.value}, Comm {order.executed.comm}"
if order.isbuy():
self.log(f"BUY EXECUTED, Price: {order_details}")
else: # Sell
self.log(f"SELL EXECUTED, Price: {order_details}")
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
self.order = None
def next(self):
current_bar_datetime = self.data.num2date(self.data.datetime[0])
previous_bar_datetime = self.data.num2date(self.data.datetime[-1])
if current_bar_datetime.date() != previous_bar_datetime.date():
self.opening_range_low = self.data.low[0]
self.opening_range_high = self.data.high[0]
self.bought_today = False
opening_range_start_time = time(9, 30, 0)
dt = datetime.combine(date.today(), opening_range_start_time) + timedelta(minutes=self.p.num_opening_bars)
opening_range_end_time = dt.time()
if current_bar_datetime.time() >= opening_range_start_time \
and current_bar_datetime.time() < opening_range_end_time:
self.opening_range_high = max(self.data.high[0], self.opening_range_high)
self.opening_range_low = min(self.data.low[0], self.opening_range_low)
self.opening_range = self.opening_range_high - self.opening_range_low
else:
if self.order:
return
if self.position and (self.data.close[0] > (self.opening_range_high + self.opening_range)):
self.close()
if self.data.close[0] > self.opening_range_high and not self.position and not self.bought_today:
self.order = self.buy()
self.bought_today = True
if self.position and (self.data.close[0] < (self.opening_range_high - self.opening_range)):
self.order = self.close()
if self.position and current_bar_datetime.time() >= time(15, 45, 0):
self.log("RUNNING OUT OF TIME - LIQUIDATING POSITION")
self.close()
def stop(self):
self.log('(Num Opening Bars %2d) Ending Value %.2f' %
(self.params.num_opening_bars, self.broker.getvalue()))
if self.broker.getvalue() > 130000:
self.log("*** BIG WINNER ***")
if self.broker.getvalue() < 70000:
self.log("*** MAJOR LOSER ***")
if __name__ == '__main__':
conn = sqlite3.connect(config.DB_FILE)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute("""
SELECT DISTINCT(stock_id) as stock_id FROM stock_price_minute
""")
stocks = cursor.fetchall()
for stock in stocks:
print(f"== Testing {stock['stock_id']} ==")
cerebro = backtrader.Cerebro()
cerebro.broker.setcash(100000.0)
cerebro.addsizer(backtrader.sizers.PercentSizer, percents=95)
dataframe = pandas.read_sql("""
select datetime, open, high, low, close, volume
from stock_price_minute
where stock_id = :stock_id
and strftime('%H:%M:%S', datetime) >= '09:30:00'
and strftime('%H:%M:%S', datetime) < '16:00:00'
order by datetime asc
""", conn, params={"stock_id": stock['stock_id']}, index_col='datetime', parse_dates=['datetime'])
data = backtrader.feeds.PandasData(dataname=dataframe)
cerebro.adddata(data)
cerebro.addstrategy(OpeningRangeBreakout)
#strats = cerebro.optstrategy(OpeningRangeBreakout, num_opening_bars=[15, 30, 60])
cerebro.run()
#cerebro.plot()