diff options
Diffstat (limited to 'tests/test_acceptance.py')
-rw-r--r-- | tests/test_acceptance.py | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/tests/test_acceptance.py b/tests/test_acceptance.py new file mode 100644 index 0000000..184489e --- /dev/null +++ b/tests/test_acceptance.py | |||
@@ -0,0 +1,280 @@ | |||
1 | from .helper import * | ||
2 | |||
3 | @unittest.skipUnless("acceptance" in limits, "Acceptance skipped") | ||
4 | class AcceptanceTest(WebMockTestCase): | ||
5 | @unittest.expectedFailure | ||
6 | def test_success_sell_only_necessary(self): | ||
7 | # FIXME: catch stdout | ||
8 | self.m.report.verbose_print = False | ||
9 | fetch_balance = { | ||
10 | "ETH": { | ||
11 | "exchange_free": D("1.0"), | ||
12 | "exchange_used": D("0.0"), | ||
13 | "exchange_total": D("1.0"), | ||
14 | "total": D("1.0"), | ||
15 | }, | ||
16 | "ETC": { | ||
17 | "exchange_free": D("4.0"), | ||
18 | "exchange_used": D("0.0"), | ||
19 | "exchange_total": D("4.0"), | ||
20 | "total": D("4.0"), | ||
21 | }, | ||
22 | "XVG": { | ||
23 | "exchange_free": D("1000.0"), | ||
24 | "exchange_used": D("0.0"), | ||
25 | "exchange_total": D("1000.0"), | ||
26 | "total": D("1000.0"), | ||
27 | }, | ||
28 | } | ||
29 | repartition = { | ||
30 | "ETH": (D("0.25"), "long"), | ||
31 | "ETC": (D("0.25"), "long"), | ||
32 | "BTC": (D("0.4"), "long"), | ||
33 | "BTD": (D("0.01"), "short"), | ||
34 | "B2X": (D("0.04"), "long"), | ||
35 | "USDT": (D("0.05"), "long"), | ||
36 | } | ||
37 | |||
38 | def fetch_ticker(symbol): | ||
39 | if symbol == "ETH/BTC": | ||
40 | return { | ||
41 | "symbol": "ETH/BTC", | ||
42 | "bid": D("0.14"), | ||
43 | "ask": D("0.16") | ||
44 | } | ||
45 | if symbol == "ETC/BTC": | ||
46 | return { | ||
47 | "symbol": "ETC/BTC", | ||
48 | "bid": D("0.002"), | ||
49 | "ask": D("0.003") | ||
50 | } | ||
51 | if symbol == "XVG/BTC": | ||
52 | return { | ||
53 | "symbol": "XVG/BTC", | ||
54 | "bid": D("0.00003"), | ||
55 | "ask": D("0.00005") | ||
56 | } | ||
57 | if symbol == "BTD/BTC": | ||
58 | return { | ||
59 | "symbol": "BTD/BTC", | ||
60 | "bid": D("0.0008"), | ||
61 | "ask": D("0.0012") | ||
62 | } | ||
63 | if symbol == "B2X/BTC": | ||
64 | return { | ||
65 | "symbol": "B2X/BTC", | ||
66 | "bid": D("0.0008"), | ||
67 | "ask": D("0.0012") | ||
68 | } | ||
69 | if symbol == "USDT/BTC": | ||
70 | raise helper.ExchangeError | ||
71 | if symbol == "BTC/USDT": | ||
72 | return { | ||
73 | "symbol": "BTC/USDT", | ||
74 | "bid": D("14000"), | ||
75 | "ask": D("16000") | ||
76 | } | ||
77 | self.fail("Shouldn't have been called with {}".format(symbol)) | ||
78 | |||
79 | market = mock.Mock() | ||
80 | market.fetch_all_balances.return_value = fetch_balance | ||
81 | market.fetch_ticker.side_effect = fetch_ticker | ||
82 | with mock.patch.object(market.Portfolio, "repartition", return_value=repartition): | ||
83 | # Action 1 | ||
84 | helper.prepare_trades(market) | ||
85 | |||
86 | balances = portfolio.BalanceStore.all | ||
87 | self.assertEqual(portfolio.Amount("ETH", 1), balances["ETH"].total) | ||
88 | self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total) | ||
89 | self.assertEqual(portfolio.Amount("XVG", 1000), balances["XVG"].total) | ||
90 | |||
91 | |||
92 | trades = portfolio.TradeStore.all | ||
93 | self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from) | ||
94 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to) | ||
95 | self.assertEqual("dispose", trades[0].action) | ||
96 | |||
97 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[1].value_from) | ||
98 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[1].value_to) | ||
99 | self.assertEqual("acquire", trades[1].action) | ||
100 | |||
101 | self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades[2].value_from) | ||
102 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[2].value_to) | ||
103 | self.assertEqual("dispose", trades[2].action) | ||
104 | |||
105 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[3].value_from) | ||
106 | self.assertEqual(portfolio.Amount("BTC", D("-0.002")), trades[3].value_to) | ||
107 | self.assertEqual("acquire", trades[3].action) | ||
108 | |||
109 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[4].value_from) | ||
110 | self.assertEqual(portfolio.Amount("BTC", D("0.008")), trades[4].value_to) | ||
111 | self.assertEqual("acquire", trades[4].action) | ||
112 | |||
113 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[5].value_from) | ||
114 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[5].value_to) | ||
115 | self.assertEqual("acquire", trades[5].action) | ||
116 | |||
117 | # Action 2 | ||
118 | portfolio.TradeStore.prepare_orders(only="dispose", compute_value=lambda x, y: x["bid"] * D("1.001")) | ||
119 | |||
120 | all_orders = portfolio.TradeStore.all_orders(state="pending") | ||
121 | self.assertEqual(2, len(all_orders)) | ||
122 | self.assertEqual(2, 3*all_orders[0].amount.value) | ||
123 | self.assertEqual(D("0.14014"), all_orders[0].rate) | ||
124 | self.assertEqual(1000, all_orders[1].amount.value) | ||
125 | self.assertEqual(D("0.00003003"), all_orders[1].rate) | ||
126 | |||
127 | |||
128 | def create_order(symbol, type, action, amount, price=None, account="exchange"): | ||
129 | self.assertEqual("limit", type) | ||
130 | if symbol == "ETH/BTC": | ||
131 | self.assertEqual("sell", action) | ||
132 | self.assertEqual(D('0.66666666'), amount) | ||
133 | self.assertEqual(D("0.14014"), price) | ||
134 | elif symbol == "XVG/BTC": | ||
135 | self.assertEqual("sell", action) | ||
136 | self.assertEqual(1000, amount) | ||
137 | self.assertEqual(D("0.00003003"), price) | ||
138 | else: | ||
139 | self.fail("I shouldn't have been called") | ||
140 | |||
141 | return { | ||
142 | "id": symbol, | ||
143 | } | ||
144 | market.create_order.side_effect = create_order | ||
145 | market.order_precision.return_value = 8 | ||
146 | |||
147 | # Action 3 | ||
148 | portfolio.TradeStore.run_orders() | ||
149 | |||
150 | self.assertEqual("open", all_orders[0].status) | ||
151 | self.assertEqual("open", all_orders[1].status) | ||
152 | |||
153 | market.fetch_order.return_value = { "status": "closed", "datetime": "2018-01-20 13:40:00" } | ||
154 | market.privatePostReturnOrderTrades.return_value = [ | ||
155 | { | ||
156 | "tradeID": 42, "type": "buy", "fee": "0.0015", | ||
157 | "date": "2017-12-30 12:00:12", "rate": "0.1", | ||
158 | "amount": "10", "total": "1" | ||
159 | } | ||
160 | ] | ||
161 | with mock.patch.object(market.time, "sleep") as sleep: | ||
162 | # Action 4 | ||
163 | helper.follow_orders(verbose=False) | ||
164 | |||
165 | sleep.assert_called_with(30) | ||
166 | |||
167 | for order in all_orders: | ||
168 | self.assertEqual("closed", order.status) | ||
169 | |||
170 | fetch_balance = { | ||
171 | "ETH": { | ||
172 | "exchange_free": D("1.0") / 3, | ||
173 | "exchange_used": D("0.0"), | ||
174 | "exchange_total": D("1.0") / 3, | ||
175 | "margin_total": 0, | ||
176 | "total": D("1.0") / 3, | ||
177 | }, | ||
178 | "BTC": { | ||
179 | "exchange_free": D("0.134"), | ||
180 | "exchange_used": D("0.0"), | ||
181 | "exchange_total": D("0.134"), | ||
182 | "margin_total": 0, | ||
183 | "total": D("0.134"), | ||
184 | }, | ||
185 | "ETC": { | ||
186 | "exchange_free": D("4.0"), | ||
187 | "exchange_used": D("0.0"), | ||
188 | "exchange_total": D("4.0"), | ||
189 | "margin_total": 0, | ||
190 | "total": D("4.0"), | ||
191 | }, | ||
192 | "XVG": { | ||
193 | "exchange_free": D("0.0"), | ||
194 | "exchange_used": D("0.0"), | ||
195 | "exchange_total": D("0.0"), | ||
196 | "margin_total": 0, | ||
197 | "total": D("0.0"), | ||
198 | }, | ||
199 | } | ||
200 | market.fetch_all_balances.return_value = fetch_balance | ||
201 | |||
202 | with mock.patch.object(market.Portfolio, "repartition", return_value=repartition): | ||
203 | # Action 5 | ||
204 | helper.prepare_trades(market, only="acquire", compute_value="average") | ||
205 | |||
206 | balances = portfolio.BalanceStore.all | ||
207 | self.assertEqual(portfolio.Amount("ETH", 1 / D("3")), balances["ETH"].total) | ||
208 | self.assertEqual(portfolio.Amount("ETC", 4), balances["ETC"].total) | ||
209 | self.assertEqual(portfolio.Amount("BTC", D("0.134")), balances["BTC"].total) | ||
210 | self.assertEqual(portfolio.Amount("XVG", 0), balances["XVG"].total) | ||
211 | |||
212 | |||
213 | trades = portfolio.TradeStore.all | ||
214 | self.assertEqual(portfolio.Amount("BTC", D("0.15")), trades[0].value_from) | ||
215 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[0].value_to) | ||
216 | self.assertEqual("dispose", trades[0].action) | ||
217 | |||
218 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[1].value_from) | ||
219 | self.assertEqual(portfolio.Amount("BTC", D("0.05")), trades[1].value_to) | ||
220 | self.assertEqual("acquire", trades[1].action) | ||
221 | |||
222 | self.assertNotIn("BTC", trades) | ||
223 | |||
224 | self.assertEqual(portfolio.Amount("BTC", D("0.04")), trades[2].value_from) | ||
225 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[2].value_to) | ||
226 | self.assertEqual("dispose", trades[2].action) | ||
227 | |||
228 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[3].value_from) | ||
229 | self.assertEqual(portfolio.Amount("BTC", D("-0.002")), trades[3].value_to) | ||
230 | self.assertEqual("acquire", trades[3].action) | ||
231 | |||
232 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[4].value_from) | ||
233 | self.assertEqual(portfolio.Amount("BTC", D("0.008")), trades[4].value_to) | ||
234 | self.assertEqual("acquire", trades[4].action) | ||
235 | |||
236 | self.assertEqual(portfolio.Amount("BTC", D("0.00")), trades[5].value_from) | ||
237 | self.assertEqual(portfolio.Amount("BTC", D("0.01")), trades[5].value_to) | ||
238 | self.assertEqual("acquire", trades[5].action) | ||
239 | |||
240 | # Action 6 | ||
241 | portfolio.TradeStore.prepare_orders(only="acquire", compute_value=lambda x, y: x["ask"]) | ||
242 | |||
243 | all_orders = portfolio.TradeStore.all_orders(state="pending") | ||
244 | self.assertEqual(4, len(all_orders)) | ||
245 | self.assertEqual(portfolio.Amount("ETC", D("12.83333333")), round(all_orders[0].amount)) | ||
246 | self.assertEqual(D("0.003"), all_orders[0].rate) | ||
247 | self.assertEqual("buy", all_orders[0].action) | ||
248 | self.assertEqual("long", all_orders[0].trade_type) | ||
249 | |||
250 | self.assertEqual(portfolio.Amount("BTD", D("1.61666666")), round(all_orders[1].amount)) | ||
251 | self.assertEqual(D("0.0012"), all_orders[1].rate) | ||
252 | self.assertEqual("sell", all_orders[1].action) | ||
253 | self.assertEqual("short", all_orders[1].trade_type) | ||
254 | |||
255 | diff = portfolio.Amount("B2X", D("19.4")/3) - all_orders[2].amount | ||
256 | self.assertAlmostEqual(0, diff.value) | ||
257 | self.assertEqual(D("0.0012"), all_orders[2].rate) | ||
258 | self.assertEqual("buy", all_orders[2].action) | ||
259 | self.assertEqual("long", all_orders[2].trade_type) | ||
260 | |||
261 | self.assertEqual(portfolio.Amount("BTC", D("0.0097")), all_orders[3].amount) | ||
262 | self.assertEqual(D("16000"), all_orders[3].rate) | ||
263 | self.assertEqual("sell", all_orders[3].action) | ||
264 | self.assertEqual("long", all_orders[3].trade_type) | ||
265 | |||
266 | # Action 6b | ||
267 | # TODO: | ||
268 | # Move balances to margin | ||
269 | |||
270 | # Action 7 | ||
271 | # TODO | ||
272 | # portfolio.TradeStore.run_orders() | ||
273 | |||
274 | with mock.patch.object(market.time, "sleep") as sleep: | ||
275 | # Action 8 | ||
276 | helper.follow_orders(verbose=False) | ||
277 | |||
278 | sleep.assert_called_with(30) | ||
279 | |||
280 | |||