forked from jesse-ai/jesse
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_broker.py
283 lines (239 loc) · 9.46 KB
/
test_broker.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
import pytest
import jesse.services.selectors as selectors
from jesse.config import config, reset_config
from jesse.enums import exchanges, timeframes, order_types, order_flags, order_roles
from jesse.exceptions import InvalidStrategy, NegativeBalance, OrderNotAllowed
from jesse.models import Position, Exchange
from jesse.routes import router
from jesse.services.broker import Broker
from jesse.store import store
position: Position = None
exchange: Exchange = None
broker: Broker = None
def set_up_without_fee(is_futures_trading=False):
reset_config()
config['env']['exchanges'][exchanges.SANDBOX]['fee'] = 0
config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [
{'asset': 'USDT', 'balance': 1000},
{'asset': 'BTC', 'balance': 0},
]
if is_futures_trading:
# used only in futures trading
config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'futures'
else:
config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'spot'
config['env']['exchanges'][exchanges.SANDBOX]['settlement_currency'] = 'USDT'
config['app']['trading_mode'] = 'backtest'
config['app']['considering_exchanges'] = ['Sandbox']
router.initiate([
{'exchange': exchanges.SANDBOX, 'symbol': 'BTC-USDT', 'timeframe': '5m', 'strategy': 'Test19'}
], [])
global position
global exchange
global broker
position = selectors.get_position(exchanges.SANDBOX, 'BTC-USDT')
position.current_price = 50
exchange = selectors.get_exchange(exchanges.SANDBOX)
broker = Broker(position, exchanges.SANDBOX, 'BTC-USDT', timeframes.MINUTE_5)
def set_up_with_fee(is_futures_trading=False):
reset_config()
config['env']['exchanges'][exchanges.SANDBOX]['fee'] = 0.002
config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [
{'asset': 'USDT', 'balance': 1000},
{'asset': 'BTC', 'balance': 0},
]
if is_futures_trading:
# used only in futures trading
config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'futures'
else:
config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'spot'
config['env']['exchanges'][exchanges.SANDBOX]['settlement_currency'] = 'USDT'
config['app']['trading_mode'] = 'backtest'
config['app']['considering_exchanges'] = ['Sandbox']
router.initiate([
{'exchange': exchanges.SANDBOX, 'symbol': 'BTC-USDT', 'timeframe': '5m', 'strategy': 'Test19'}
], [])
global position
global exchange
global broker
position = selectors.get_position(exchanges.SANDBOX, 'BTC-USDT')
position.current_price = 50
exchange = selectors.get_exchange(exchanges.SANDBOX)
broker = Broker(position, exchanges.SANDBOX, 'BTC-USDT',
timeframes.MINUTE_5)
def test_cancel_all_orders():
set_up_without_fee(is_futures_trading=True)
# create 3 ACTIVE orders
o1 = broker.buy_at(1, 40)
o2 = broker.buy_at(1, 41)
o3 = broker.buy_at(1, 42)
assert o1.is_active
assert o2.is_active
assert o3.is_active
# create 2 EXECUTED orders
o4 = broker.buy_at_market(1)
# fake it
store.orders.execute_pending_market_orders()
o5 = broker.buy_at_market(2)
# fake it
store.orders.execute_pending_market_orders()
assert o4.is_executed
assert o5.is_executed
broker.cancel_all_orders()
# ACTIVE orders should have been canceled
assert o1.is_active is False
assert o2.is_active is False
assert o3.is_active is False
assert o1.is_canceled is True
assert o2.is_canceled is True
assert o3.is_canceled is True
# already-executed orders should have remain the same
assert o4.is_executed
assert o5.is_executed
assert o4.is_canceled is False
assert o5.is_canceled is False
def test_limit_orders():
set_up_with_fee()
assert exchange.assets['USDT'] == 1000
assert exchange.available_assets['USDT'] == 1000
order = broker.buy_at(1, 40)
assert order.price == 40
assert order.qty == 1
assert order.type == 'LIMIT'
assert exchange.available_assets['USDT'] == 959.92
assert exchange.assets['USDT'] == 1000
broker.cancel_order(order.id)
assert exchange.assets['USDT'] == 1000
assert exchange.available_assets['USDT'] == 1000
# buy again, this time execute
order = broker.buy_at(1, 40)
assert exchange.available_assets['USDT'] == 959.92
order.execute()
assert exchange.assets['BTC'] == 1
assert exchange.available_assets['BTC'] == 1
assert exchange.assets['USDT'] == 959.92
order = broker.sell_at(1, 60)
assert order.price == 60
assert order.qty == -1
assert order.type == 'LIMIT'
assert exchange.assets['USDT'] == 959.92
assert exchange.available_assets['BTC'] == 0
assert exchange.assets['BTC'] == 1
broker.cancel_order(order.id)
assert exchange.assets['BTC'] == 1
assert exchange.assets['USDT'] == 959.92
assert exchange.available_assets['USDT'] == 959.92
order = broker.sell_at(1, 60)
order.execute()
assert exchange.assets['BTC'] == 0
assert exchange.available_assets['BTC'] == 0
assert exchange.assets['USDT'] == 1019.8
assert exchange.available_assets['USDT'] == 1019.8
# validate that when qty for base asset is 0 (spot market)
with pytest.raises(NegativeBalance):
broker.sell_at(1, 60)
# validation: qty cannot be 0
with pytest.raises(InvalidStrategy):
broker.sell_at(0, 100)
broker.buy_at(0, 100)
def test_opening_and_closing_position_with_stop():
set_up_without_fee(is_futures_trading=True)
assert position.current_price == 50
assert position.is_close is True
assert exchange.assets['USDT'] == 1000
assert exchange.available_margin() == 1000
assert exchange.wallet_balance() == 1000
# open position
open_position_order = broker.start_profit_at('buy', 1, 60, order_roles.OPEN_POSITION)
open_position_order.execute()
position.current_price = 60
assert position.is_open is True
assert position.entry_price == 60
assert position.qty == 1
assert exchange.assets['USDT'] == 1000
assert exchange.wallet_balance() == 1000
assert exchange.available_margin() == 940
# submit stop-loss order
stop_loss_order = broker.reduce_position_at(1, 40, order_roles.CLOSE_POSITION)
assert stop_loss_order.flag == order_flags.REDUCE_ONLY
# balance should NOT have changed
assert exchange.assets['USDT'] == 1000
assert exchange.wallet_balance() == 1000
# submit take-profit order also
take_profit_order = broker.reduce_position_at(1, 80, order_roles.CLOSE_POSITION)
assert take_profit_order.flag == order_flags.REDUCE_ONLY
assert exchange.assets['USDT'] == 1000
# execute stop order
stop_loss_order.execute()
position.current_price = 40
assert exchange.assets['USDT'] == 980
assert exchange.wallet_balance() == 980
assert exchange.available_margin() == 980
take_profit_order.cancel()
assert exchange.available_margin() == 980
assert position.is_close is True
assert position.entry_price is None
assert position.exit_price == 40
def test_start_profit():
set_up_without_fee()
assert position.current_price == 50
assert position.is_close is True
assert exchange.assets['USDT'] == 1000
assert exchange.available_assets['USDT'] == 1000
order = broker.start_profit_at('buy', 1, 60)
assert exchange.available_assets['USDT'] == 940
assert exchange.assets['USDT'] == 1000
assert order.type == order_types.STOP
assert order.qty == 1
order.cancel()
assert exchange.assets['USDT'] == 1000
assert exchange.available_assets['USDT'] == 1000
order = broker.start_profit_at('buy', 1, 60)
assert exchange.available_assets['USDT'] == 940
assert exchange.assets['USDT'] == 1000
order.execute()
assert exchange.assets['USDT'] == 940
assert exchange.available_assets['USDT'] == 940
assert position.qty == 1
assert position.entry_price == 60
# validation: qty cannot be 0
with pytest.raises(InvalidStrategy):
broker.start_profit_at('buy', 0, 100)
def test_stop_loss():
set_up_without_fee(is_futures_trading=True)
assert position.current_price == 50
assert position.is_close is True
assert exchange.available_margin() == 1000
assert exchange.wallet_balance() == 1000
# open position
broker.buy_at_market(1)
# fake it
store.orders.execute_pending_market_orders()
assert position.is_open is True
assert position.entry_price == 50
assert position.qty == 1
assert exchange.available_margin() == 950
# even executed orders should not affect wallet_balance unless it's for reducing positon
assert exchange.wallet_balance() == 1000
order = broker.reduce_position_at(1, 40)
assert order.type == order_types.STOP
assert order.price == 40
assert order.qty == -1
assert order.side == 'sell'
assert order.flag == order_flags.REDUCE_ONLY
# balance should NOT have changed
assert exchange.available_margin() == 950
assert exchange.wallet_balance() == 1000
# execute stop order
order.execute()
assert position.is_close is True
assert position.entry_price is None
assert position.exit_price == 40
assert exchange.available_margin() == 990
assert exchange.wallet_balance() == 990
def test_should_not_submit_reduce_only_orders_when_position_is_closed():
set_up_without_fee(is_futures_trading=True)
with pytest.raises(OrderNotAllowed):
broker.reduce_position_at(1, 20)
with pytest.raises(OrderNotAllowed):
broker.reduce_position_at(1, 20)