4 from decimal
import Decimal
as D
5 from unittest
import mock
8 from io
import StringIO
11 limits
= ["acceptance", "unit"]
12 for test_type
in limits
:
13 if "--no{}".format(test_type
) in sys
.argv
:
14 sys
.argv
.remove("--no{}".format(test_type
))
15 limits
.remove(test_type
)
16 if "--only{}".format(test_type
) in sys
.argv
:
17 sys
.argv
.remove("--only{}".format(test_type
))
21 class WebMockTestCase(unittest
.TestCase
):
25 super(WebMockTestCase
, self
).setUp()
26 self
.wm
= requests_mock
.Mocker()
30 mock
.patch
.multiple(portfolio
.ReportStore
,
31 logs
=[], verbose_print
=True),
32 mock
.patch
.multiple(portfolio
.BalanceStore
,
34 mock
.patch
.multiple(portfolio
.TradeStore
,
37 mock
.patch
.multiple(portfolio
.Portfolio
, last_date
=None, data
=None, liquidities
={}),
38 mock
.patch
.multiple(portfolio
.Computation
,
39 computations
=portfolio
.Computation
.computations
),
40 mock
.patch
.multiple(helper
,
43 ticker_cache_timestamp
=self
.time
.time()),
45 for patcher
in self
.patchers
:
49 for patcher
in self
.patchers
:
52 super(WebMockTestCase
, self
).tearDown()
54 @unittest.skipUnless("unit" in limits
, "Unit skipped")
55 class PortfolioTest(WebMockTestCase
):
57 if self
.json_response
is not None:
58 portfolio
.Portfolio
.data
= self
.json_response
61 super(PortfolioTest
, self
).setUp()
63 with open("test_portfolio.json") as example
:
64 self
.json_response
= example
.read()
66 self
.wm
.get(portfolio
.Portfolio
.URL
, text
=self
.json_response
)
68 @mock.patch("portfolio.ReportStore")
69 def test_get_cryptoportfolio(self
, report_store
):
70 self
.wm
.get(portfolio
.Portfolio
.URL
, [
71 {"text":'{ "foo": "bar" }
', "status_code": 200},
72 {"text": "System Error", "status_code": 500},
73 {"exc": requests.exceptions.ConnectTimeout},
75 portfolio.Portfolio.get_cryptoportfolio()
76 self.assertIn("foo", portfolio.Portfolio.data)
77 self.assertEqual("bar", portfolio.Portfolio.data["foo"])
78 self.assertTrue(self.wm.called)
79 self.assertEqual(1, self.wm.call_count)
80 report_store.log_error.assert_not_called()
81 report_store.log_http_request.assert_called_once()
82 report_store.log_http_request.reset_mock()
84 portfolio.Portfolio.get_cryptoportfolio()
85 self.assertIsNone(portfolio.Portfolio.data)
86 self.assertEqual(2, self.wm.call_count)
87 report_store.log_error.assert_not_called()
88 report_store.log_http_request.assert_called_once()
89 report_store.log_http_request.reset_mock()
92 portfolio.Portfolio.data = "Foo"
93 portfolio.Portfolio.get_cryptoportfolio()
94 self.assertEqual("Foo", portfolio.Portfolio.data)
95 self.assertEqual(3, self.wm.call_count)
96 report_store.log_error.assert_called_once_with("get_cryptoportfolio",
98 report_store.log_http_request.assert_not_called()
100 @mock.patch("portfolio.ReportStore")
101 def test_parse_cryptoportfolio(self, report_store):
102 portfolio.Portfolio.parse_cryptoportfolio()
104 self.assertListEqual(
106 list(portfolio.Portfolio.liquidities.keys()))
108 liquidities = portfolio.Portfolio.liquidities
109 self.assertEqual(10, len(liquidities["medium"].keys()))
110 self.assertEqual(10, len(liquidities["high"].keys()))
113 'BTC
': (D("0.2857"), "long"),
114 'DGB
': (D("0.1015"), "long"),
115 'DOGE
': (D("0.1805"), "long"),
116 'SC
': (D("0.0623"), "long"),
117 'ZEC
': (D("0.3701"), "long"),
119 date = portfolio.datetime(2018, 1, 8)
120 self.assertDictEqual(expected, liquidities["high"][date])
123 'BTC
': (D("1.1102e-16"), "long"),
124 'ETC
': (D("0.1"), "long"),
125 'FCT
': (D("0.1"), "long"),
126 'GAS
': (D("0.1"), "long"),
127 'NAV
': (D("0.1"), "long"),
128 'OMG
': (D("0.1"), "long"),
129 'OMNI
': (D("0.1"), "long"),
130 'PPC
': (D("0.1"), "long"),
131 'RIC
': (D("0.1"), "long"),
132 'VIA
': (D("0.1"), "long"),
133 'XCP
': (D("0.1"), "long"),
135 self.assertDictEqual(expected, liquidities["medium"][date])
136 self.assertEqual(portfolio.datetime(2018, 1, 15), portfolio.Portfolio.last_date)
138 report_store.log_http_request.assert_called_once_with("GET",
139 portfolio.Portfolio.URL, None, mock.ANY, mock.ANY)
140 report_store.log_http_request.reset_mock()
142 # It doesn't refetch the data when available
143 portfolio
.Portfolio
.parse_cryptoportfolio()
144 report_store
.log_http_request
.assert_not_called()
146 self
.assertEqual(1, self
.wm
.call_count
)
148 portfolio
.Portfolio
.parse_cryptoportfolio(refetch
=True)
149 self
.assertEqual(2, self
.wm
.call_count
)
150 report_store
.log_http_request
.assert_called_once()
152 @mock.patch("portfolio.ReportStore")
153 def test_repartition(self
, report_store
):
155 'BTC': (D("1.1102e-16"), "long"),
156 'USDT': (D("0.1"), "long"),
157 'ETC': (D("0.1"), "long"),
158 'FCT': (D("0.1"), "long"),
159 'OMG': (D("0.1"), "long"),
160 'STEEM': (D("0.1"), "long"),
161 'STRAT': (D("0.1"), "long"),
162 'XEM': (D("0.1"), "long"),
163 'XMR': (D("0.1"), "long"),
164 'XVC': (D("0.1"), "long"),
165 'ZRX': (D("0.1"), "long"),
168 'USDT': (D("0.1226"), "long"),
169 'BTC': (D("0.1429"), "long"),
170 'ETC': (D("0.1127"), "long"),
171 'ETH': (D("0.1569"), "long"),
172 'FCT': (D("0.3341"), "long"),
173 'GAS': (D("0.1308"), "long"),
176 self
.assertEqual(expected_medium
, portfolio
.Portfolio
.repartition())
177 self
.assertEqual(expected_medium
, portfolio
.Portfolio
.repartition(liquidity
="medium"))
178 self
.assertEqual(expected_high
, portfolio
.Portfolio
.repartition(liquidity
="high"))
180 self
.assertEqual(1, self
.wm
.call_count
)
182 portfolio
.Portfolio
.repartition()
183 self
.assertEqual(1, self
.wm
.call_count
)
185 portfolio
.Portfolio
.repartition(refetch
=True)
186 self
.assertEqual(2, self
.wm
.call_count
)
187 report_store
.log_http_request
.assert_called()
188 self
.assertEqual(2, report_store
.log_http_request
.call_count
)
190 @mock.patch.object(portfolio
.time
, "sleep")
191 @mock.patch.object(portfolio
.Portfolio
, "repartition")
192 def test_wait_for_recent(self
, repartition
, sleep
):
194 def _repartition(refetch
):
195 self
.assertTrue(refetch
)
197 portfolio
.Portfolio
.last_date
= portfolio
.datetime
.now()\
198 - portfolio
.timedelta(10)\
199 + portfolio
.timedelta(self
.call_count
)
200 repartition
.side_effect
= _repartition
202 portfolio
.Portfolio
.wait_for_recent()
203 sleep
.assert_called_with(30)
204 self
.assertEqual(6, sleep
.call_count
)
205 self
.assertEqual(7, repartition
.call_count
)
208 repartition
.reset_mock()
209 portfolio
.Portfolio
.last_date
= None
211 portfolio
.Portfolio
.wait_for_recent(delta
=15)
212 sleep
.assert_not_called()
213 self
.assertEqual(1, repartition
.call_count
)
216 repartition
.reset_mock()
217 portfolio
.Portfolio
.last_date
= None
219 portfolio
.Portfolio
.wait_for_recent(delta
=1)
220 sleep
.assert_called_with(30)
221 self
.assertEqual(9, sleep
.call_count
)
222 self
.assertEqual(10, repartition
.call_count
)
224 @unittest.skipUnless("unit" in limits
, "Unit skipped")
225 class AmountTest(WebMockTestCase
):
226 def test_values(self
):
227 amount
= portfolio
.Amount("BTC", "0.65")
228 self
.assertEqual(D("0.65"), amount
.value
)
229 self
.assertEqual("BTC", amount
.currency
)
231 def test_in_currency(self
):
232 amount
= portfolio
.Amount("ETC", 10)
234 self
.assertEqual(amount
, amount
.in_currency("ETC", None))
236 ticker_mock
= unittest
.mock
.Mock()
237 with mock
.patch
.object(helper
, 'get_ticker', new
=ticker_mock
):
238 ticker_mock
.return_value
= None
240 self
.assertRaises(Exception, amount
.in_currency
, "ETH", None)
242 with mock
.patch
.object(helper
, 'get_ticker', new
=ticker_mock
):
243 ticker_mock
.return_value
= {
249 converted_amount
= amount
.in_currency("ETH", None)
251 self
.assertEqual(D("3.0"), converted_amount
.value
)
252 self
.assertEqual("ETH", converted_amount
.currency
)
253 self
.assertEqual(amount
, converted_amount
.linked_to
)
254 self
.assertEqual("bar", converted_amount
.ticker
["foo"])
256 converted_amount
= amount
.in_currency("ETH", None, action
="bid", compute_value
="default")
257 self
.assertEqual(D("2"), converted_amount
.value
)
259 converted_amount
= amount
.in_currency("ETH", None, compute_value
="ask")
260 self
.assertEqual(D("4"), converted_amount
.value
)
262 converted_amount
= amount
.in_currency("ETH", None, rate
=D("0.02"))
263 self
.assertEqual(D("0.2"), converted_amount
.value
)
265 def test__round(self
):
266 amount
= portfolio
.Amount("BAR", portfolio
.D("1.23456789876"))
267 self
.assertEqual(D("1.23456789"), round(amount
).value
)
268 self
.assertEqual(D("1.23"), round(amount
, 2).value
)
271 amount
= portfolio
.Amount("SC", -120)
272 self
.assertEqual(120, abs(amount
).value
)
273 self
.assertEqual("SC", abs(amount
).currency
)
275 amount
= portfolio
.Amount("SC", 10)
276 self
.assertEqual(10, abs(amount
).value
)
277 self
.assertEqual("SC", abs(amount
).currency
)
280 amount1
= portfolio
.Amount("XVG", "12.9")
281 amount2
= portfolio
.Amount("XVG", "13.1")
283 self
.assertEqual(26, (amount1
+ amount2
).value
)
284 self
.assertEqual("XVG", (amount1
+ amount2
).currency
)
286 amount3
= portfolio
.Amount("ETH", "1.6")
287 with self
.assertRaises(Exception):
290 amount4
= portfolio
.Amount("ETH", 0.0)
291 self
.assertEqual(amount1
, amount1
+ amount4
)
293 self
.assertEqual(amount1
, amount1
+ 0)
295 def test__radd(self
):
296 amount
= portfolio
.Amount("XVG", "12.9")
298 self
.assertEqual(amount
, 0 + amount
)
299 with self
.assertRaises(Exception):
303 amount1
= portfolio
.Amount("XVG", "13.3")
304 amount2
= portfolio
.Amount("XVG", "13.1")
306 self
.assertEqual(D("0.2"), (amount1
- amount2
).value
)
307 self
.assertEqual("XVG", (amount1
- amount2
).currency
)
309 amount3
= portfolio
.Amount("ETH", "1.6")
310 with self
.assertRaises(Exception):
313 amount4
= portfolio
.Amount("ETH", 0.0)
314 self
.assertEqual(amount1
, amount1
- amount4
)
316 def test__rsub(self
):
317 amount
= portfolio
.Amount("ETH", "1.6")
318 with self
.assertRaises(Exception):
321 self
.assertEqual(portfolio
.Amount("ETH", "-1.6"), -amount
)
324 amount
= portfolio
.Amount("XEM", 11)
326 self
.assertEqual(D("38.5"), (amount
* D("3.5")).value
)
327 self
.assertEqual(D("33"), (amount
* 3).value
)
329 with self
.assertRaises(Exception):
332 def test__rmul(self
):
333 amount
= portfolio
.Amount("XEM", 11)
335 self
.assertEqual(D("38.5"), (D("3.5") * amount
).value
)
336 self
.assertEqual(D("33"), (3 * amount
).value
)
338 def test__floordiv(self
):
339 amount
= portfolio
.Amount("XEM", 11)
341 self
.assertEqual(D("5.5"), (amount
/ 2).value
)
342 self
.assertEqual(D("4.4"), (amount
/ D("2.5")).value
)
344 with self
.assertRaises(Exception):
347 def test__truediv(self
):
348 amount
= portfolio
.Amount("XEM", 11)
350 self
.assertEqual(D("5.5"), (amount
/ 2).value
)
351 self
.assertEqual(D("4.4"), (amount
/ D("2.5")).value
)
354 amount1
= portfolio
.Amount("BTD", 11.3)
355 amount2
= portfolio
.Amount("BTD", 13.1)
357 self
.assertTrue(amount1
< amount2
)
358 self
.assertFalse(amount2
< amount1
)
359 self
.assertFalse(amount1
< amount1
)
361 amount3
= portfolio
.Amount("BTC", 1.6)
362 with self
.assertRaises(Exception):
366 amount1
= portfolio
.Amount("BTD", 11.3)
367 amount2
= portfolio
.Amount("BTD", 13.1)
369 self
.assertTrue(amount1
<= amount2
)
370 self
.assertFalse(amount2
<= amount1
)
371 self
.assertTrue(amount1
<= amount1
)
373 amount3
= portfolio
.Amount("BTC", 1.6)
374 with self
.assertRaises(Exception):
378 amount1
= portfolio
.Amount("BTD", 11.3)
379 amount2
= portfolio
.Amount("BTD", 13.1)
381 self
.assertTrue(amount2
> amount1
)
382 self
.assertFalse(amount1
> amount2
)
383 self
.assertFalse(amount1
> amount1
)
385 amount3
= portfolio
.Amount("BTC", 1.6)
386 with self
.assertRaises(Exception):
390 amount1
= portfolio
.Amount("BTD", 11.3)
391 amount2
= portfolio
.Amount("BTD", 13.1)
393 self
.assertTrue(amount2
>= amount1
)
394 self
.assertFalse(amount1
>= amount2
)
395 self
.assertTrue(amount1
>= amount1
)
397 amount3
= portfolio
.Amount("BTC", 1.6)
398 with self
.assertRaises(Exception):
402 amount1
= portfolio
.Amount("BTD", 11.3)
403 amount2
= portfolio
.Amount("BTD", 13.1)
404 amount3
= portfolio
.Amount("BTD", 11.3)
406 self
.assertFalse(amount1
== amount2
)
407 self
.assertFalse(amount2
== amount1
)
408 self
.assertTrue(amount1
== amount3
)
409 self
.assertFalse(amount2
== 0)
411 amount4
= portfolio
.Amount("BTC", 1.6)
412 with self
.assertRaises(Exception):
415 amount5
= portfolio
.Amount("BTD", 0)
416 self
.assertTrue(amount5
== 0)
419 amount1
= portfolio
.Amount("BTD", 11.3)
420 amount2
= portfolio
.Amount("BTD", 13.1)
421 amount3
= portfolio
.Amount("BTD", 11.3)
423 self
.assertTrue(amount1
!= amount2
)
424 self
.assertTrue(amount2
!= amount1
)
425 self
.assertFalse(amount1
!= amount3
)
426 self
.assertTrue(amount2
!= 0)
428 amount4
= portfolio
.Amount("BTC", 1.6)
429 with self
.assertRaises(Exception):
432 amount5
= portfolio
.Amount("BTD", 0)
433 self
.assertFalse(amount5
!= 0)
436 amount1
= portfolio
.Amount("BTD", "11.3")
438 self
.assertEqual(portfolio
.D("-11.3"), (-amount1
).value
)
441 amount1
= portfolio
.Amount("BTX", 32)
442 self
.assertEqual("32.00000000 BTX", str(amount1
))
444 amount2
= portfolio
.Amount("USDT", 12000)
445 amount1
.linked_to
= amount2
446 self
.assertEqual("32.00000000 BTX [12000.00000000 USDT]", str(amount1
))
448 def test__repr(self
):
449 amount1
= portfolio
.Amount("BTX", 32)
450 self
.assertEqual("Amount(32.00000000 BTX)", repr(amount1
))
452 amount2
= portfolio
.Amount("USDT", 12000)
453 amount1
.linked_to
= amount2
454 self
.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT))", repr(amount1
))
456 amount3
= portfolio
.Amount("BTC", 0.1)
457 amount2
.linked_to
= amount3
458 self
.assertEqual("Amount(32.00000000 BTX -> Amount(12000.00000000 USDT -> Amount(0.10000000 BTC)))", repr(amount1
))
460 def test_as_json(self
):
461 amount
= portfolio
.Amount("BTX", 32)
462 self
.assertEqual({"currency": "BTX", "value": D("32")}
, amount
.as_json())
464 amount
= portfolio
.Amount("BTX", "1E-10")
465 self
.assertEqual({"currency": "BTX", "value": D("0")}
, amount
.as_json())
467 amount
= portfolio
.Amount("BTX", "1E-5")
468 self
.assertEqual({"currency": "BTX", "value": D("0.00001")}
, amount
.as_json())
469 self
.assertEqual("0.00001", str(amount
.as_json()["value"]))
471 @unittest.skipUnless("unit" in limits
, "Unit skipped")
472 class BalanceTest(WebMockTestCase
):
473 def test_values(self
):
474 balance
= portfolio
.Balance("BTC", {
475 "exchange_total": "0.65",
476 "exchange_free": "0.35",
477 "exchange_used": "0.30",
478 "margin_total": "-10",
479 "margin_borrowed": "-10",
481 "margin_position_type": "short",
482 "margin_borrowed_base_currency": "USDT",
483 "margin_liquidation_price": "1.20",
484 "margin_pending_gain": "10",
485 "margin_lending_fees": "0.4",
486 "margin_borrowed_base_price": "0.15",
488 self
.assertEqual(portfolio
.D("0.65"), balance
.exchange_total
.value
)
489 self
.assertEqual(portfolio
.D("0.35"), balance
.exchange_free
.value
)
490 self
.assertEqual(portfolio
.D("0.30"), balance
.exchange_used
.value
)
491 self
.assertEqual("BTC", balance
.exchange_total
.currency
)
492 self
.assertEqual("BTC", balance
.exchange_free
.currency
)
493 self
.assertEqual("BTC", balance
.exchange_total
.currency
)
495 self
.assertEqual(portfolio
.D("-10"), balance
.margin_total
.value
)
496 self
.assertEqual(portfolio
.D("-10"), balance
.margin_borrowed
.value
)
497 self
.assertEqual(portfolio
.D("0"), balance
.margin_free
.value
)
498 self
.assertEqual("BTC", balance
.margin_total
.currency
)
499 self
.assertEqual("BTC", balance
.margin_borrowed
.currency
)
500 self
.assertEqual("BTC", balance
.margin_free
.currency
)
502 self
.assertEqual("BTC", balance
.currency
)
504 self
.assertEqual(portfolio
.D("0.4"), balance
.margin_lending_fees
.value
)
505 self
.assertEqual("USDT", balance
.margin_lending_fees
.currency
)
507 def test__repr(self
):
508 self
.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX])",
509 repr(portfolio
.Balance("BTX", { "exchange_free": 2, "exchange_total": 2 }
)))
510 balance
= portfolio
.Balance("BTX", { "exchange_total": 3,
511 "exchange_used": 1, "exchange_free": 2 })
512 self
.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX + ❌1.00000000 BTX = 3.00000000 BTX])", repr(balance
))
514 balance
= portfolio
.Balance("BTX", { "exchange_total": 1, "exchange_used": 1}
)
515 self
.assertEqual("Balance(BTX Exch: [❌1.00000000 BTX])", repr(balance
))
517 balance
= portfolio
.Balance("BTX", { "margin_total": 3,
518 "margin_borrowed": 1, "margin_free": 2 })
519 self
.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + borrowed 1.00000000 BTX = 3.00000000 BTX])", repr(balance
))
521 balance
= portfolio
.Balance("BTX", { "margin_total": 2, "margin_free": 2 }
)
522 self
.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX])", repr(balance
))
524 balance
= portfolio
.Balance("BTX", { "margin_total": -3,
525 "margin_borrowed_base_price": D("0.1"),
526 "margin_borrowed_base_currency": "BTC",
527 "margin_lending_fees": D("0.002") })
528 self
.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance
))
530 balance
= portfolio
.Balance("BTX", { "margin_total": 1,
531 "margin_borrowed": 1, "exchange_free": 2, "exchange_total": 2})
532 self
.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX] Margin: [borrowed 1.00000000 BTX] Total: [0.00000000 BTX])", repr(balance
))
534 def test_as_json(self
):
535 balance
= portfolio
.Balance("BTX", { "exchange_free": 2, "exchange_total": 2 }
)
536 as_json
= balance
.as_json()
537 self
.assertEqual(set(portfolio
.Balance
.base_keys
), set(as_json
.keys()))
538 self
.assertEqual(D(0), as_json
["total"])
539 self
.assertEqual(D(2), as_json
["exchange_total"])
540 self
.assertEqual(D(2), as_json
["exchange_free"])
541 self
.assertEqual(D(0), as_json
["exchange_used"])
542 self
.assertEqual(D(0), as_json
["margin_total"])
543 self
.assertEqual(D(0), as_json
["margin_free"])
544 self
.assertEqual(D(0), as_json
["margin_borrowed"])
546 @unittest.skipUnless("unit" in limits
, "Unit skipped")
547 class HelperTest(WebMockTestCase
):
548 def test_get_ticker(self
):
550 market
.fetch_ticker
.side_effect
= [
551 { "bid": 1, "ask": 3 }
,
552 helper
.ExchangeError("foo"),
553 { "bid": 10, "ask": 40 }
,
554 helper
.ExchangeError("foo"),
555 helper
.ExchangeError("foo"),
558 ticker
= helper
.get_ticker("ETH", "ETC", market
)
559 market
.fetch_ticker
.assert_called_with("ETH/ETC")
560 self
.assertEqual(1, ticker
["bid"])
561 self
.assertEqual(3, ticker
["ask"])
562 self
.assertEqual(2, ticker
["average"])
563 self
.assertFalse(ticker
["inverted"])
565 ticker
= helper
.get_ticker("ETH", "XVG", market
)
566 self
.assertEqual(0.0625, ticker
["average"])
567 self
.assertTrue(ticker
["inverted"])
568 self
.assertIn("original", ticker
)
569 self
.assertEqual(10, ticker
["original"]["bid"])
571 ticker
= helper
.get_ticker("XVG", "XMR", market
)
572 self
.assertIsNone(ticker
)
574 market
.fetch_ticker
.assert_has_calls([
575 mock
.call("ETH/ETC"),
576 mock
.call("ETH/XVG"),
577 mock
.call("XVG/ETH"),
578 mock
.call("XVG/XMR"),
579 mock
.call("XMR/XVG"),
582 market2
= mock
.Mock()
583 market2
.fetch_ticker
.side_effect
= [
584 { "bid": 1, "ask": 3 }
,
585 { "bid": 1.2, "ask": 3.5 }
,
587 ticker1
= helper
.get_ticker("ETH", "ETC", market2
)
588 ticker2
= helper
.get_ticker("ETH", "ETC", market2
)
589 ticker3
= helper
.get_ticker("ETC", "ETH", market2
)
590 market2
.fetch_ticker
.assert_called_once_with("ETH/ETC")
591 self
.assertEqual(1, ticker1
["bid"])
592 self
.assertDictEqual(ticker1
, ticker2
)
593 self
.assertDictEqual(ticker1
, ticker3
["original"])
595 ticker4
= helper
.get_ticker("ETH", "ETC", market2
, refresh
=True)
596 ticker5
= helper
.get_ticker("ETH", "ETC", market2
)
597 self
.assertEqual(1.2, ticker4
["bid"])
598 self
.assertDictEqual(ticker4
, ticker5
)
600 market3
= mock
.Mock()
601 market3
.fetch_ticker
.side_effect
= [
602 { "bid": 1, "ask": 3 }
,
603 { "bid": 1.2, "ask": 3.5 }
,
605 ticker6
= helper
.get_ticker("ETH", "ETC", market3
)
606 helper
.ticker_cache_timestamp
-= 4
607 ticker7
= helper
.get_ticker("ETH", "ETC", market3
)
608 helper
.ticker_cache_timestamp
-= 2
609 ticker8
= helper
.get_ticker("ETH", "ETC", market3
)
610 self
.assertDictEqual(ticker6
, ticker7
)
611 self
.assertEqual(1.2, ticker8
["bid"])
613 def test_fetch_fees(self
):
615 market
.fetch_fees
.return_value
= "Foo"
616 self
.assertEqual("Foo", helper
.fetch_fees(market
))
617 market
.fetch_fees
.assert_called_once()
618 self
.assertEqual("Foo", helper
.fetch_fees(market
))
619 market
.fetch_fees
.assert_called_once()
621 @mock.patch.object(portfolio
.Portfolio
, "repartition")
622 @mock.patch.object(helper
, "get_ticker")
623 @mock.patch.object(portfolio
.TradeStore
, "compute_trades")
624 @mock.patch("store.ReportStore")
625 @mock.patch("helper.ReportStore")
626 def test_prepare_trades(self
, report_store_h
, report_store
, compute_trades
, get_ticker
, repartition
):
627 repartition
.return_value
= {
628 "XEM": (D("0.75"), "long"),
629 "BTC": (D("0.25"), "long"),
631 def _get_ticker(c1
, c2
, market
):
632 if c1
== "USDT" and c2
== "BTC":
633 return { "average": D("0.0001") }
634 if c1
== "XVG" and c2
== "BTC":
635 return { "average": D("0.000001") }
636 if c1
== "XEM" and c2
== "BTC":
637 return { "average": D("0.001") }
638 self
.fail("Should be called with {}, {}".format(c1
, c2
))
639 get_ticker
.side_effect
= _get_ticker
642 market
.fetch_all_balances
.return_value
= {
644 "exchange_free": D("10000.0"),
645 "exchange_used": D("0.0"),
646 "exchange_total": D("10000.0"),
647 "total": D("10000.0")
650 "exchange_free": D("10000.0"),
651 "exchange_used": D("0.0"),
652 "exchange_total": D("10000.0"),
653 "total": D("10000.0")
656 helper
.prepare_trades(market
)
657 compute_trades
.assert_called()
659 call
= compute_trades
.call_args
660 self
.assertEqual(market
, call
[1]["market"])
661 self
.assertEqual(1, call
[0][0]["USDT"].value
)
662 self
.assertEqual(D("0.01"), call
[0][0]["XVG"].value
)
663 self
.assertEqual(D("0.2525"), call
[0][1]["BTC"].value
)
664 self
.assertEqual(D("0.7575"), call
[0][1]["XEM"].value
)
665 report_store_h
.log_stage
.assert_called_once_with("prepare_trades")
666 report_store
.log_balances
.assert_called_once_with(market
)
668 @mock.patch.object(portfolio
.Portfolio
, "repartition")
669 @mock.patch.object(helper
, "get_ticker")
670 @mock.patch.object(portfolio
.TradeStore
, "compute_trades")
671 @mock.patch("store.ReportStore")
672 @mock.patch("helper.ReportStore")
673 def test_update_trades(self
, report_store_h
, report_store
, compute_trades
, get_ticker
, repartition
):
674 repartition
.return_value
= {
675 "XEM": (D("0.75"), "long"),
676 "BTC": (D("0.25"), "long"),
678 def _get_ticker(c1
, c2
, market
):
679 if c1
== "USDT" and c2
== "BTC":
680 return { "average": D("0.0001") }
681 if c1
== "XVG" and c2
== "BTC":
682 return { "average": D("0.000001") }
683 if c1
== "XEM" and c2
== "BTC":
684 return { "average": D("0.001") }
685 self
.fail("Should be called with {}, {}".format(c1
, c2
))
686 get_ticker
.side_effect
= _get_ticker
689 market
.fetch_all_balances
.return_value
= {
691 "exchange_free": D("10000.0"),
692 "exchange_used": D("0.0"),
693 "exchange_total": D("10000.0"),
694 "total": D("10000.0")
697 "exchange_free": D("10000.0"),
698 "exchange_used": D("0.0"),
699 "exchange_total": D("10000.0"),
700 "total": D("10000.0")
703 helper
.update_trades(market
)
704 compute_trades
.assert_called()
706 call
= compute_trades
.call_args
707 self
.assertEqual(market
, call
[1]["market"])
708 self
.assertEqual(1, call
[0][0]["USDT"].value
)
709 self
.assertEqual(D("0.01"), call
[0][0]["XVG"].value
)
710 self
.assertEqual(D("0.2525"), call
[0][1]["BTC"].value
)
711 self
.assertEqual(D("0.7575"), call
[0][1]["XEM"].value
)
712 report_store_h
.log_stage
.assert_called_once_with("update_trades")
713 report_store
.log_balances
.assert_called_once_with(market
)
715 @mock.patch.object(portfolio
.Portfolio
, "repartition")
716 @mock.patch.object(helper
, "get_ticker")
717 @mock.patch.object(portfolio
.TradeStore
, "compute_trades")
718 @mock.patch("store.ReportStore")
719 @mock.patch("helper.ReportStore")
720 def test_prepare_trades_to_sell_all(self
, report_store_h
, report_store
, compute_trades
, get_ticker
, repartition
):
721 def _get_ticker(c1
, c2
, market
):
722 if c1
== "USDT" and c2
== "BTC":
723 return { "average": D("0.0001") }
724 if c1
== "XVG" and c2
== "BTC":
725 return { "average": D("0.000001") }
726 self
.fail("Should be called with {}, {}".format(c1
, c2
))
727 get_ticker
.side_effect
= _get_ticker
730 market
.fetch_all_balances
.return_value
= {
732 "exchange_free": D("10000.0"),
733 "exchange_used": D("0.0"),
734 "exchange_total": D("10000.0"),
735 "total": D("10000.0")
738 "exchange_free": D("10000.0"),
739 "exchange_used": D("0.0"),
740 "exchange_total": D("10000.0"),
741 "total": D("10000.0")
744 helper
.prepare_trades_to_sell_all(market
)
745 repartition
.assert_not_called()
746 compute_trades
.assert_called()
748 call
= compute_trades
.call_args
749 self
.assertEqual(market
, call
[1]["market"])
750 self
.assertEqual(1, call
[0][0]["USDT"].value
)
751 self
.assertEqual(D("0.01"), call
[0][0]["XVG"].value
)
752 self
.assertEqual(D("1.01"), call
[0][1]["BTC"].value
)
753 report_store_h
.log_stage
.assert_called_once_with("prepare_trades_to_sell_all")
754 report_store
.log_balances
.assert_called_once_with(market
)
756 @mock.patch.object(portfolio
.time
, "sleep")
757 @mock.patch.object(portfolio
.TradeStore
, "all_orders")
758 def test_follow_orders(self
, all_orders
, time_mock
):
759 for debug
, sleep
in [
760 (False, None), (True, None),
761 (False, 12), (True, 12)]:
762 with self
.subTest(sleep
=sleep
, debug
=debug
), \
763 mock
.patch("helper.ReportStore") as report_store
:
764 portfolio
.TradeStore
.debug
= debug
765 order_mock1
= mock
.Mock()
766 order_mock2
= mock
.Mock()
767 order_mock3
= mock
.Mock()
768 all_orders
.side_effect
= [
769 [order_mock1
, order_mock2
],
770 [order_mock1
, order_mock2
],
772 [order_mock1
, order_mock3
],
773 [order_mock1
, order_mock3
],
775 [order_mock1
, order_mock3
],
776 [order_mock1
, order_mock3
],
781 order_mock1
.get_status
.side_effect
= ["open", "open", "closed"]
782 order_mock2
.get_status
.side_effect
= ["open"]
783 order_mock3
.get_status
.side_effect
= ["open", "closed"]
785 order_mock1
.trade
= mock
.Mock()
786 order_mock2
.trade
= mock
.Mock()
787 order_mock3
.trade
= mock
.Mock()
789 helper
.follow_orders(sleep
=sleep
)
791 order_mock1
.trade
.update_order
.assert_any_call(order_mock1
, 1)
792 order_mock1
.trade
.update_order
.assert_any_call(order_mock1
, 2)
793 self
.assertEqual(2, order_mock1
.trade
.update_order
.call_count
)
794 self
.assertEqual(3, order_mock1
.get_status
.call_count
)
796 order_mock2
.trade
.update_order
.assert_any_call(order_mock2
, 1)
797 self
.assertEqual(1, order_mock2
.trade
.update_order
.call_count
)
798 self
.assertEqual(1, order_mock2
.get_status
.call_count
)
800 order_mock3
.trade
.update_order
.assert_any_call(order_mock3
, 2)
801 self
.assertEqual(1, order_mock3
.trade
.update_order
.call_count
)
802 self
.assertEqual(2, order_mock3
.get_status
.call_count
)
803 report_store
.log_stage
.assert_called()
805 mock
.call("follow_orders_begin"),
806 mock
.call("follow_orders_tick_1"),
807 mock
.call("follow_orders_tick_2"),
808 mock
.call("follow_orders_tick_3"),
809 mock
.call("follow_orders_end"),
811 report_store
.log_stage
.assert_has_calls(calls
)
812 report_store
.log_orders
.assert_called()
813 self
.assertEqual(3, report_store
.log_orders
.call_count
)
815 mock
.call([order_mock1
, order_mock2
], tick
=1),
816 mock
.call([order_mock1
, order_mock3
], tick
=2),
817 mock
.call([order_mock1
, order_mock3
], tick
=3),
819 report_store
.log_orders
.assert_has_calls(calls
)
821 mock
.call(order_mock1
, 3, finished
=True),
822 mock
.call(order_mock3
, 3, finished
=True),
824 report_store
.log_order
.assert_has_calls(calls
)
828 report_store
.log_debug_action
.assert_called_with("Set follow_orders tick to 7s")
829 time_mock
.assert_called_with(7)
831 time_mock
.assert_called_with(30)
833 time_mock
.assert_called_with(sleep
)
835 @mock.patch.object(portfolio
.BalanceStore
, "fetch_balances")
836 def test_move_balance(self
, fetch_balances
):
837 for debug
in [True, False]:
838 with self
.subTest(debug
=debug
),\
839 mock
.patch("helper.ReportStore") as report_store
:
840 value_from
= portfolio
.Amount("BTC", "1.0")
841 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
842 value_to
= portfolio
.Amount("BTC", "10.0")
843 trade1
= portfolio
.Trade(value_from
, value_to
, "ETH")
845 value_from
= portfolio
.Amount("BTC", "0.0")
846 value_from
.linked_to
= portfolio
.Amount("ETH", "0.0")
847 value_to
= portfolio
.Amount("BTC", "-3.0")
848 trade2
= portfolio
.Trade(value_from
, value_to
, "ETH")
850 value_from
= portfolio
.Amount("USDT", "0.0")
851 value_from
.linked_to
= portfolio
.Amount("XVG", "0.0")
852 value_to
= portfolio
.Amount("USDT", "-50.0")
853 trade3
= portfolio
.Trade(value_from
, value_to
, "XVG")
855 portfolio
.TradeStore
.all
= [trade1
, trade2
, trade3
]
856 balance1
= portfolio
.Balance("BTC", { "margin_free": "0" }
)
857 balance2
= portfolio
.Balance("USDT", { "margin_free": "100" }
)
858 balance3
= portfolio
.Balance("ETC", { "margin_free": "10" }
)
859 portfolio
.BalanceStore
.all
= {"BTC": balance1, "USDT": balance2, "ETC": balance3}
863 helper
.move_balances(market
, debug
=debug
)
865 fetch_balances
.assert_called_with(market
)
866 report_store
.log_move_balances
.assert_called_once()
869 report_store
.log_debug_action
.assert_called()
870 self
.assertEqual(3, report_store
.log_debug_action
.call_count
)
872 market
.transfer_balance
.assert_any_call("BTC", 3, "exchange", "margin")
873 market
.transfer_balance
.assert_any_call("USDT", 50, "margin", "exchange")
874 market
.transfer_balance
.assert_any_call("ETC", 10, "margin", "exchange")
876 @mock.patch.object(helper
, "prepare_trades")
877 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
878 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
879 def test_print_orders(self
, log_stage
, prepare_orders
, prepare_trades
):
881 portfolio
.BalanceStore
.all
= {
882 "BTC": portfolio
.Balance("BTC", {
884 "exchange_total":"0.65",
885 "exchange_free": "0.35",
886 "exchange_used": "0.30"}),
887 "ETH": portfolio
.Balance("ETH", {
891 "exchange_used": 0}),
893 helper
.print_orders(market
)
894 prepare_trades
.assert_called_with(market
, base_currency
="BTC",
895 compute_value
="average", debug
=True)
896 prepare_orders
.assert_called_with(compute_value
="average")
897 log_stage
.assert_called_with("print_orders")
899 @mock.patch.object(portfolio
.BalanceStore
, "fetch_balances")
900 @mock.patch.object(portfolio
.BalanceStore
, "in_currency")
901 @mock.patch.object(helper
.ReportStore
, "print_log")
902 def test_print_balances(self
, print_log
, in_currency
, fetch_balances
):
904 portfolio
.BalanceStore
.all
= {
905 "BTC": portfolio
.Balance("BTC", {
907 "exchange_total":"0.65",
908 "exchange_free": "0.35",
909 "exchange_used": "0.30"}),
910 "ETH": portfolio
.Balance("ETH", {
914 "exchange_used": 0}),
916 in_currency
.return_value
= {
917 "BTC": portfolio
.Amount("BTC", "0.65"),
918 "ETH": portfolio
.Amount("BTC", "0.3"),
920 helper
.print_balances(market
)
921 fetch_balances
.assert_called_with(market
)
922 print_log
.assert_has_calls([
924 mock
.call(portfolio
.Amount("BTC", "0.95")),
927 @mock.patch.object(helper
, "prepare_trades")
928 @mock.patch.object(helper
, "follow_orders")
929 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
930 @mock.patch.object(portfolio
.TradeStore
, "run_orders")
931 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
932 def test_process_sell_needed__1_sell(self
, log_stage
, run_orders
,
933 prepare_orders
, follow_orders
, prepare_trades
):
935 portfolio
.BalanceStore
.all
= {
936 "BTC": portfolio
.Balance("BTC", {
938 "exchange_total":"0.65",
939 "exchange_free": "0.35",
940 "exchange_used": "0.30"}),
941 "ETH": portfolio
.Balance("ETH", {
945 "exchange_used": 0}),
947 helper
.process_sell_needed__1_sell(market
)
948 prepare_trades
.assert_called_with(market
, base_currency
="BTC",
949 liquidity
="medium", debug
=False)
950 prepare_orders
.assert_called_with(compute_value
="average",
952 run_orders
.assert_called()
953 follow_orders
.assert_called()
954 log_stage
.assert_called_with("process_sell_needed__1_sell_end")
956 @mock.patch.object(helper
, "update_trades")
957 @mock.patch.object(helper
, "follow_orders")
958 @mock.patch.object(helper
, "move_balances")
959 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
960 @mock.patch.object(portfolio
.TradeStore
, "run_orders")
961 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
962 def test_process_sell_needed__2_buy(self
, log_stage
, run_orders
,
963 prepare_orders
, move_balances
,
964 follow_orders
, update_trades
):
966 portfolio
.BalanceStore
.all
= {
967 "BTC": portfolio
.Balance("BTC", {
969 "exchange_total":"0.65",
970 "exchange_free": "0.35",
971 "exchange_used": "0.30"}),
972 "ETH": portfolio
.Balance("ETH", {
976 "exchange_used": 0}),
978 helper
.process_sell_needed__2_buy(market
)
979 update_trades
.assert_called_with(market
, base_currency
="BTC",
980 debug
=False, liquidity
="medium", only
="acquire")
981 prepare_orders
.assert_called_with(compute_value
="average",
983 move_balances
.assert_called_with(market
, debug
=False)
984 run_orders
.assert_called()
985 follow_orders
.assert_called()
986 log_stage
.assert_called_with("process_sell_needed__2_buy_end")
988 @mock.patch.object(helper
, "prepare_trades_to_sell_all")
989 @mock.patch.object(helper
, "follow_orders")
990 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
991 @mock.patch.object(portfolio
.TradeStore
, "run_orders")
992 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
993 def test_process_sell_all__1_sell(self
, log_stage
, run_orders
,
994 prepare_orders
, follow_orders
, prepare_trades_to_sell_all
):
996 portfolio
.BalanceStore
.all
= {
997 "BTC": portfolio
.Balance("BTC", {
999 "exchange_total":"0.65",
1000 "exchange_free": "0.35",
1001 "exchange_used": "0.30"}),
1002 "ETH": portfolio
.Balance("ETH", {
1004 "exchange_total": 3,
1006 "exchange_used": 0}),
1008 helper
.process_sell_all__1_all_sell(market
)
1009 prepare_trades_to_sell_all
.assert_called_with(market
, base_currency
="BTC",
1011 prepare_orders
.assert_called_with(compute_value
="average")
1012 run_orders
.assert_called()
1013 follow_orders
.assert_called()
1014 log_stage
.assert_called_with("process_sell_all__1_all_sell_end")
1016 @mock.patch.object(helper
, "prepare_trades")
1017 @mock.patch.object(helper
, "follow_orders")
1018 @mock.patch.object(helper
, "move_balances")
1019 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
1020 @mock.patch.object(portfolio
.TradeStore
, "run_orders")
1021 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
1022 def test_process_sell_all__2_all_buy(self
, log_stage
, run_orders
,
1023 prepare_orders
, move_balances
, follow_orders
,
1025 market
= mock
.Mock()
1026 portfolio
.BalanceStore
.all
= {
1027 "BTC": portfolio
.Balance("BTC", {
1029 "exchange_total":"0.65",
1030 "exchange_free": "0.35",
1031 "exchange_used": "0.30"}),
1032 "ETH": portfolio
.Balance("ETH", {
1034 "exchange_total": 3,
1036 "exchange_used": 0}),
1038 helper
.process_sell_all__2_all_buy(market
)
1039 prepare_trades
.assert_called_with(market
, base_currency
="BTC",
1040 liquidity
="medium", debug
=False)
1041 prepare_orders
.assert_called_with(compute_value
="average")
1042 move_balances
.assert_called_with(market
, debug
=False)
1043 run_orders
.assert_called()
1044 follow_orders
.assert_called()
1045 log_stage
.assert_called_with("process_sell_all__2_all_buy_end")
1048 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1049 class TradeStoreTest(WebMockTestCase
):
1050 @mock.patch.object(portfolio
.BalanceStore
, "currencies")
1051 @mock.patch.object(portfolio
.TradeStore
, "trade_if_matching")
1052 @mock.patch.object(portfolio
.ReportStore
, "log_trades")
1053 def test_compute_trades(self
, log_trades
, trade_if_matching
, currencies
):
1054 currencies
.return_value
= ["XMR", "DASH", "XVG", "BTC", "ETH"]
1057 "XMR": portfolio
.Amount("BTC", D("0.9")),
1058 "DASH": portfolio
.Amount("BTC", D("0.4")),
1059 "XVG": portfolio
.Amount("BTC", D("-0.5")),
1060 "BTC": portfolio
.Amount("BTC", D("0.5")),
1063 "DASH": portfolio
.Amount("BTC", D("0.5")),
1064 "XVG": portfolio
.Amount("BTC", D("0.1")),
1065 "BTC": portfolio
.Amount("BTC", D("0.4")),
1066 "ETH": portfolio
.Amount("BTC", D("0.3")),
1075 trade_if_matching
.side_effect
= side_effect
1077 portfolio
.TradeStore
.compute_trades(values_in_base
,
1078 new_repartition
, only
="only", market
="market")
1080 self
.assertEqual(5, trade_if_matching
.call_count
)
1081 self
.assertEqual(3, len(portfolio
.TradeStore
.all
))
1082 self
.assertEqual([1, 4, 5], portfolio
.TradeStore
.all
)
1083 log_trades
.assert_called_with(side_effect
, "only", False)
1085 def test_trade_if_matching(self
):
1086 result
= portfolio
.TradeStore
.trade_if_matching(
1087 portfolio
.Amount("BTC", D("0")),
1088 portfolio
.Amount("BTC", D("0.3")),
1089 "ETH", only
="nope", market
="market"
1091 self
.assertEqual(False, result
[0])
1092 self
.assertIsInstance(result
[1], portfolio
.Trade
)
1094 portfolio
.TradeStore
.all
= []
1095 result
= portfolio
.TradeStore
.trade_if_matching(
1096 portfolio
.Amount("BTC", D("0")),
1097 portfolio
.Amount("BTC", D("0.3")),
1098 "ETH", only
=None, market
="market"
1100 self
.assertEqual(True, result
[0])
1102 portfolio
.TradeStore
.all
= []
1103 result
= portfolio
.TradeStore
.trade_if_matching(
1104 portfolio
.Amount("BTC", D("0")),
1105 portfolio
.Amount("BTC", D("0.3")),
1106 "ETH", only
="acquire", market
="market"
1108 self
.assertEqual(True, result
[0])
1110 portfolio
.TradeStore
.all
= []
1111 result
= portfolio
.TradeStore
.trade_if_matching(
1112 portfolio
.Amount("BTC", D("0")),
1113 portfolio
.Amount("BTC", D("0.3")),
1114 "ETH", only
="dispose", market
="market"
1116 self
.assertEqual(False, result
[0])
1118 @mock.patch.object(portfolio
.ReportStore
, "log_orders")
1119 def test_prepare_orders(self
, log_orders
):
1120 trade_mock1
= mock
.Mock()
1121 trade_mock2
= mock
.Mock()
1123 trade_mock1
.prepare_order
.return_value
= 1
1124 trade_mock2
.prepare_order
.return_value
= 2
1126 portfolio
.TradeStore
.all
.append(trade_mock1
)
1127 portfolio
.TradeStore
.all
.append(trade_mock2
)
1129 portfolio
.TradeStore
.prepare_orders()
1130 trade_mock1
.prepare_order
.assert_called_with(compute_value
="default")
1131 trade_mock2
.prepare_order
.assert_called_with(compute_value
="default")
1132 log_orders
.assert_called_once_with([1, 2], None, "default")
1134 log_orders
.reset_mock()
1136 portfolio
.TradeStore
.prepare_orders(compute_value
="bla")
1137 trade_mock1
.prepare_order
.assert_called_with(compute_value
="bla")
1138 trade_mock2
.prepare_order
.assert_called_with(compute_value
="bla")
1139 log_orders
.assert_called_once_with([1, 2], None, "bla")
1141 trade_mock1
.prepare_order
.reset_mock()
1142 trade_mock2
.prepare_order
.reset_mock()
1143 log_orders
.reset_mock()
1145 trade_mock1
.action
= "foo"
1146 trade_mock2
.action
= "bar"
1147 portfolio
.TradeStore
.prepare_orders(only
="bar")
1148 trade_mock1
.prepare_order
.assert_not_called()
1149 trade_mock2
.prepare_order
.assert_called_with(compute_value
="default")
1150 log_orders
.assert_called_once_with([2], "bar", "default")
1152 def test_print_all_with_order(self
):
1153 trade_mock1
= mock
.Mock()
1154 trade_mock2
= mock
.Mock()
1155 trade_mock3
= mock
.Mock()
1156 portfolio
.TradeStore
.all
= [trade_mock1
, trade_mock2
, trade_mock3
]
1158 portfolio
.TradeStore
.print_all_with_order()
1160 trade_mock1
.print_with_order
.assert_called()
1161 trade_mock2
.print_with_order
.assert_called()
1162 trade_mock3
.print_with_order
.assert_called()
1164 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
1165 @mock.patch.object(portfolio
.ReportStore
, "log_orders")
1166 @mock.patch.object(portfolio
.TradeStore
, "all_orders")
1167 def test_run_orders(self
, all_orders
, log_orders
, log_stage
):
1168 order_mock1
= mock
.Mock()
1169 order_mock2
= mock
.Mock()
1170 order_mock3
= mock
.Mock()
1171 all_orders
.return_value
= [order_mock1
, order_mock2
, order_mock3
]
1172 portfolio
.TradeStore
.run_orders()
1173 all_orders
.assert_called_with(state
="pending")
1175 order_mock1
.run
.assert_called()
1176 order_mock2
.run
.assert_called()
1177 order_mock3
.run
.assert_called()
1179 log_stage
.assert_called_with("run_orders")
1180 log_orders
.assert_called_with([order_mock1
, order_mock2
,
1183 def test_all_orders(self
):
1184 trade_mock1
= mock
.Mock()
1185 trade_mock2
= mock
.Mock()
1187 order_mock1
= mock
.Mock()
1188 order_mock2
= mock
.Mock()
1189 order_mock3
= mock
.Mock()
1191 trade_mock1
.orders
= [order_mock1
, order_mock2
]
1192 trade_mock2
.orders
= [order_mock3
]
1194 order_mock1
.status
= "pending"
1195 order_mock2
.status
= "open"
1196 order_mock3
.status
= "open"
1198 portfolio
.TradeStore
.all
.append(trade_mock1
)
1199 portfolio
.TradeStore
.all
.append(trade_mock2
)
1201 orders
= portfolio
.TradeStore
.all_orders()
1202 self
.assertEqual(3, len(orders
))
1204 open_orders
= portfolio
.TradeStore
.all_orders(state
="open")
1205 self
.assertEqual(2, len(open_orders
))
1206 self
.assertEqual([order_mock2
, order_mock3
], open_orders
)
1208 @mock.patch.object(portfolio
.TradeStore
, "all_orders")
1209 def test_update_all_orders_status(self
, all_orders
):
1210 order_mock1
= mock
.Mock()
1211 order_mock2
= mock
.Mock()
1212 order_mock3
= mock
.Mock()
1213 all_orders
.return_value
= [order_mock1
, order_mock2
, order_mock3
]
1214 portfolio
.TradeStore
.update_all_orders_status()
1215 all_orders
.assert_called_with(state
="open")
1217 order_mock1
.get_status
.assert_called()
1218 order_mock2
.get_status
.assert_called()
1219 order_mock3
.get_status
.assert_called()
1222 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1223 class BalanceStoreTest(WebMockTestCase
):
1225 super(BalanceStoreTest
, self
).setUp()
1227 self
.fetch_balance
= {
1231 "exchange_total": 0,
1235 "exchange_free": D("6.0"),
1236 "exchange_used": D("1.2"),
1237 "exchange_total": D("7.2"),
1241 "exchange_free": 16,
1243 "exchange_total": 16,
1249 "exchange_total": 0,
1250 "margin_total": D("-1.0"),
1255 @mock.patch.object(helper
, "get_ticker")
1256 @mock.patch("portfolio.ReportStore.log_tickers")
1257 def test_in_currency(self
, log_tickers
, get_ticker
):
1258 portfolio
.BalanceStore
.all
= {
1259 "BTC": portfolio
.Balance("BTC", {
1261 "exchange_total":"0.65",
1262 "exchange_free": "0.35",
1263 "exchange_used": "0.30"}),
1264 "ETH": portfolio
.Balance("ETH", {
1266 "exchange_total": 3,
1268 "exchange_used": 0}),
1270 market
= mock
.Mock()
1271 get_ticker
.return_value
= {
1274 "average": D("0.1"),
1277 amounts
= portfolio
.BalanceStore
.in_currency("BTC", market
)
1278 self
.assertEqual("BTC", amounts
["ETH"].currency
)
1279 self
.assertEqual(D("0.65"), amounts
["BTC"].value
)
1280 self
.assertEqual(D("0.30"), amounts
["ETH"].value
)
1281 log_tickers
.assert_called_once_with(market
, amounts
, "BTC",
1283 log_tickers
.reset_mock()
1285 amounts
= portfolio
.BalanceStore
.in_currency("BTC", market
, compute_value
="bid")
1286 self
.assertEqual(D("0.65"), amounts
["BTC"].value
)
1287 self
.assertEqual(D("0.27"), amounts
["ETH"].value
)
1288 log_tickers
.assert_called_once_with(market
, amounts
, "BTC",
1290 log_tickers
.reset_mock()
1292 amounts
= portfolio
.BalanceStore
.in_currency("BTC", market
, compute_value
="bid", type="exchange_used")
1293 self
.assertEqual(D("0.30"), amounts
["BTC"].value
)
1294 self
.assertEqual(0, amounts
["ETH"].value
)
1295 log_tickers
.assert_called_once_with(market
, amounts
, "BTC",
1296 "bid", "exchange_used")
1297 log_tickers
.reset_mock()
1299 @mock.patch.object(portfolio
.ReportStore
, "log_balances")
1300 def test_fetch_balances(self
, log_balances
):
1301 market
= mock
.Mock()
1302 market
.fetch_all_balances
.return_value
= self
.fetch_balance
1304 portfolio
.BalanceStore
.fetch_balances(market
)
1305 self
.assertNotIn("ETC", portfolio
.BalanceStore
.currencies())
1306 self
.assertListEqual(["USDT", "XVG", "XMR"], list(portfolio
.BalanceStore
.currencies()))
1308 portfolio
.BalanceStore
.all
["ETC"] = portfolio
.Balance("ETC", {
1309 "exchange_total": "1", "exchange_free": "0",
1310 "exchange_used": "1" })
1311 portfolio
.BalanceStore
.fetch_balances(market
)
1312 self
.assertEqual(0, portfolio
.BalanceStore
.all
["ETC"].total
)
1313 self
.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio
.BalanceStore
.currencies()))
1314 log_balances
.assert_called_with(market
)
1316 @mock.patch.object(portfolio
.Portfolio
, "repartition")
1317 @mock.patch.object(portfolio
.ReportStore
, "log_balances")
1318 @mock.patch("store.ReportStore.log_dispatch")
1319 def test_dispatch_assets(self
, log_dispatch
, log_balances
, repartition
):
1320 market
= mock
.Mock()
1321 market
.fetch_all_balances
.return_value
= self
.fetch_balance
1322 portfolio
.BalanceStore
.fetch_balances(market
)
1324 self
.assertNotIn("XEM", portfolio
.BalanceStore
.currencies())
1326 repartition_hash
= {
1327 "XEM": (D("0.75"), "long"),
1328 "BTC": (D("0.26"), "long"),
1329 "DASH": (D("0.10"), "short"),
1331 repartition
.return_value
= repartition_hash
1333 amounts
= portfolio
.BalanceStore
.dispatch_assets(portfolio
.Amount("BTC", "11.1"))
1334 repartition
.assert_called_with(liquidity
="medium")
1335 self
.assertIn("XEM", portfolio
.BalanceStore
.currencies())
1336 self
.assertEqual(D("2.6"), amounts
["BTC"].value
)
1337 self
.assertEqual(D("7.5"), amounts
["XEM"].value
)
1338 self
.assertEqual(D("-1.0"), amounts
["DASH"].value
)
1339 log_balances
.assert_called_with(market
)
1340 log_dispatch
.assert_called_once_with(portfolio
.Amount("BTC",
1341 "11.1"), amounts
, "medium", repartition_hash
)
1343 def test_currencies(self
):
1344 portfolio
.BalanceStore
.all
= {
1345 "BTC": portfolio
.Balance("BTC", {
1347 "exchange_total":"0.65",
1348 "exchange_free": "0.35",
1349 "exchange_used": "0.30"}),
1350 "ETH": portfolio
.Balance("ETH", {
1352 "exchange_total": 3,
1354 "exchange_used": 0}),
1356 self
.assertListEqual(["BTC", "ETH"], list(portfolio
.BalanceStore
.currencies()))
1358 def test_as_json(self
):
1359 balance_mock1
= mock
.Mock()
1360 balance_mock1
.as_json
.return_value
= 1
1362 balance_mock2
= mock
.Mock()
1363 balance_mock2
.as_json
.return_value
= 2
1365 portfolio
.BalanceStore
.all
= {
1366 "BTC": balance_mock1
,
1367 "ETH": balance_mock2
,
1370 as_json
= portfolio
.BalanceStore
.as_json()
1371 self
.assertEqual(1, as_json
["BTC"])
1372 self
.assertEqual(2, as_json
["ETH"])
1375 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1376 class ComputationTest(WebMockTestCase
):
1377 def test_compute_value(self
):
1378 compute
= mock
.Mock()
1379 portfolio
.Computation
.compute_value("foo", "buy", compute_value
=compute
)
1380 compute
.assert_called_with("foo", "ask")
1382 compute
.reset_mock()
1383 portfolio
.Computation
.compute_value("foo", "sell", compute_value
=compute
)
1384 compute
.assert_called_with("foo", "bid")
1386 compute
.reset_mock()
1387 portfolio
.Computation
.compute_value("foo", "ask", compute_value
=compute
)
1388 compute
.assert_called_with("foo", "ask")
1390 compute
.reset_mock()
1391 portfolio
.Computation
.compute_value("foo", "bid", compute_value
=compute
)
1392 compute
.assert_called_with("foo", "bid")
1394 compute
.reset_mock()
1395 portfolio
.Computation
.computations
["test"] = compute
1396 portfolio
.Computation
.compute_value("foo", "bid", compute_value
="test")
1397 compute
.assert_called_with("foo", "bid")
1400 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1401 class TradeTest(WebMockTestCase
):
1403 def test_values_assertion(self
):
1404 value_from
= portfolio
.Amount("BTC", "1.0")
1405 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1406 value_to
= portfolio
.Amount("BTC", "1.0")
1407 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1408 self
.assertEqual("BTC", trade
.base_currency
)
1409 self
.assertEqual("ETH", trade
.currency
)
1411 with self
.assertRaises(AssertionError):
1412 portfolio
.Trade(value_from
, value_to
, "ETC")
1413 with self
.assertRaises(AssertionError):
1414 value_from
.linked_to
= None
1415 portfolio
.Trade(value_from
, value_to
, "ETH")
1416 with self
.assertRaises(AssertionError):
1417 value_from
.currency
= "ETH"
1418 portfolio
.Trade(value_from
, value_to
, "ETH")
1420 value_from
= portfolio
.Amount("BTC", 0)
1421 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1422 self
.assertEqual(0, trade
.value_from
.linked_to
)
1424 def test_action(self
):
1425 value_from
= portfolio
.Amount("BTC", "1.0")
1426 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1427 value_to
= portfolio
.Amount("BTC", "1.0")
1428 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1430 self
.assertIsNone(trade
.action
)
1432 value_from
= portfolio
.Amount("BTC", "1.0")
1433 value_from
.linked_to
= portfolio
.Amount("BTC", "1.0")
1434 value_to
= portfolio
.Amount("BTC", "2.0")
1435 trade
= portfolio
.Trade(value_from
, value_to
, "BTC")
1437 self
.assertIsNone(trade
.action
)
1439 value_from
= portfolio
.Amount("BTC", "0.5")
1440 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1441 value_to
= portfolio
.Amount("BTC", "1.0")
1442 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1444 self
.assertEqual("acquire", trade
.action
)
1446 value_from
= portfolio
.Amount("BTC", "0")
1447 value_from
.linked_to
= portfolio
.Amount("ETH", "0")
1448 value_to
= portfolio
.Amount("BTC", "-1.0")
1449 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1451 self
.assertEqual("acquire", trade
.action
)
1453 def test_order_action(self
):
1454 value_from
= portfolio
.Amount("BTC", "0.5")
1455 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1456 value_to
= portfolio
.Amount("BTC", "1.0")
1457 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1459 self
.assertEqual("buy", trade
.order_action(False))
1460 self
.assertEqual("sell", trade
.order_action(True))
1462 value_from
= portfolio
.Amount("BTC", "0")
1463 value_from
.linked_to
= portfolio
.Amount("ETH", "0")
1464 value_to
= portfolio
.Amount("BTC", "-1.0")
1465 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1467 self
.assertEqual("sell", trade
.order_action(False))
1468 self
.assertEqual("buy", trade
.order_action(True))
1470 def test_trade_type(self
):
1471 value_from
= portfolio
.Amount("BTC", "0.5")
1472 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1473 value_to
= portfolio
.Amount("BTC", "1.0")
1474 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1476 self
.assertEqual("long", trade
.trade_type
)
1478 value_from
= portfolio
.Amount("BTC", "0")
1479 value_from
.linked_to
= portfolio
.Amount("ETH", "0")
1480 value_to
= portfolio
.Amount("BTC", "-1.0")
1481 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1483 self
.assertEqual("short", trade
.trade_type
)
1485 def test_filled_amount(self
):
1486 value_from
= portfolio
.Amount("BTC", "0.5")
1487 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1488 value_to
= portfolio
.Amount("BTC", "1.0")
1489 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1491 order1
= mock
.Mock()
1492 order1
.filled_amount
.return_value
= portfolio
.Amount("ETH", "0.3")
1494 order2
= mock
.Mock()
1495 order2
.filled_amount
.return_value
= portfolio
.Amount("ETH", "0.01")
1496 trade
.orders
.append(order1
)
1497 trade
.orders
.append(order2
)
1499 self
.assertEqual(portfolio
.Amount("ETH", "0.31"), trade
.filled_amount())
1500 order1
.filled_amount
.assert_called_with(in_base_currency
=False)
1501 order2
.filled_amount
.assert_called_with(in_base_currency
=False)
1503 self
.assertEqual(portfolio
.Amount("ETH", "0.31"), trade
.filled_amount(in_base_currency
=False))
1504 order1
.filled_amount
.assert_called_with(in_base_currency
=False)
1505 order2
.filled_amount
.assert_called_with(in_base_currency
=False)
1507 self
.assertEqual(portfolio
.Amount("ETH", "0.31"), trade
.filled_amount(in_base_currency
=True))
1508 order1
.filled_amount
.assert_called_with(in_base_currency
=True)
1509 order2
.filled_amount
.assert_called_with(in_base_currency
=True)
1511 @mock.patch.object(helper
, "get_ticker")
1512 @mock.patch.object(portfolio
.Computation
, "compute_value")
1513 @mock.patch.object(portfolio
.Trade
, "filled_amount")
1514 @mock.patch.object(portfolio
, "Order")
1515 def test_prepare_order(self
, Order
, filled_amount
, compute_value
, get_ticker
):
1516 Order
.return_value
= "Order"
1518 with self
.subTest(desc
="Nothing to do"):
1519 value_from
= portfolio
.Amount("BTC", "10")
1520 value_from
.rate
= D("0.1")
1521 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1522 value_to
= portfolio
.Amount("BTC", "10")
1523 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1525 trade
.prepare_order()
1527 filled_amount
.assert_not_called()
1528 compute_value
.assert_not_called()
1529 self
.assertEqual(0, len(trade
.orders
))
1530 Order
.assert_not_called()
1532 get_ticker
.return_value
= { "inverted": False }
1533 with self
.subTest(desc
="Already filled"),\
1534 mock
.patch("portfolio.ReportStore") as report_store
:
1535 filled_amount
.return_value
= portfolio
.Amount("FOO", "100")
1536 compute_value
.return_value
= D("0.125")
1538 value_from
= portfolio
.Amount("BTC", "10")
1539 value_from
.rate
= D("0.1")
1540 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1541 value_to
= portfolio
.Amount("BTC", "0")
1542 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1544 trade
.prepare_order()
1546 filled_amount
.assert_called_with(in_base_currency
=False)
1547 compute_value
.assert_called_with(get_ticker
.return_value
, "sell", compute_value
="default")
1548 self
.assertEqual(0, len(trade
.orders
))
1549 report_store
.log_error
.assert_called_with("prepare_order", message
=mock
.ANY
)
1550 Order
.assert_not_called()
1552 with self
.subTest(action
="dispose", inverted
=False):
1553 filled_amount
.return_value
= portfolio
.Amount("FOO", "60")
1554 compute_value
.return_value
= D("0.125")
1556 value_from
= portfolio
.Amount("BTC", "10")
1557 value_from
.rate
= D("0.1")
1558 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1559 value_to
= portfolio
.Amount("BTC", "1")
1560 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1562 trade
.prepare_order()
1564 filled_amount
.assert_called_with(in_base_currency
=False)
1565 compute_value
.assert_called_with(get_ticker
.return_value
, "sell", compute_value
="default")
1566 self
.assertEqual(1, len(trade
.orders
))
1567 Order
.assert_called_with("sell", portfolio
.Amount("FOO", 30),
1568 D("0.125"), "BTC", "long", "market",
1569 trade
, close_if_possible
=False)
1571 with self
.subTest(action
="acquire", inverted
=False):
1572 filled_amount
.return_value
= portfolio
.Amount("BTC", "3")
1573 compute_value
.return_value
= D("0.125")
1575 value_from
= portfolio
.Amount("BTC", "1")
1576 value_from
.rate
= D("0.1")
1577 value_from
.linked_to
= portfolio
.Amount("FOO", "10")
1578 value_to
= portfolio
.Amount("BTC", "10")
1579 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1581 trade
.prepare_order()
1583 filled_amount
.assert_called_with(in_base_currency
=True)
1584 compute_value
.assert_called_with(get_ticker
.return_value
, "buy", compute_value
="default")
1585 self
.assertEqual(1, len(trade
.orders
))
1587 Order
.assert_called_with("buy", portfolio
.Amount("FOO", 48),
1588 D("0.125"), "BTC", "long", "market",
1589 trade
, close_if_possible
=False)
1591 with self
.subTest(close_if_possible
=True):
1592 filled_amount
.return_value
= portfolio
.Amount("FOO", "0")
1593 compute_value
.return_value
= D("0.125")
1595 value_from
= portfolio
.Amount("BTC", "10")
1596 value_from
.rate
= D("0.1")
1597 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1598 value_to
= portfolio
.Amount("BTC", "0")
1599 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1601 trade
.prepare_order()
1603 filled_amount
.assert_called_with(in_base_currency
=False)
1604 compute_value
.assert_called_with(get_ticker
.return_value
, "sell", compute_value
="default")
1605 self
.assertEqual(1, len(trade
.orders
))
1606 Order
.assert_called_with("sell", portfolio
.Amount("FOO", 100),
1607 D("0.125"), "BTC", "long", "market",
1608 trade
, close_if_possible
=True)
1610 get_ticker
.return_value
= { "inverted": True, "original": {}
}
1611 with self
.subTest(action
="dispose", inverted
=True):
1612 filled_amount
.return_value
= portfolio
.Amount("FOO", "300")
1613 compute_value
.return_value
= D("125")
1615 value_from
= portfolio
.Amount("BTC", "10")
1616 value_from
.rate
= D("0.01")
1617 value_from
.linked_to
= portfolio
.Amount("FOO", "1000")
1618 value_to
= portfolio
.Amount("BTC", "1")
1619 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1621 trade
.prepare_order(compute_value
="foo")
1623 filled_amount
.assert_called_with(in_base_currency
=True)
1624 compute_value
.assert_called_with(get_ticker
.return_value
["original"], "buy", compute_value
="foo")
1625 self
.assertEqual(1, len(trade
.orders
))
1626 Order
.assert_called_with("buy", portfolio
.Amount("BTC", D("4.8")),
1627 D("125"), "FOO", "long", "market",
1628 trade
, close_if_possible
=False)
1630 with self
.subTest(action
="acquire", inverted
=True):
1631 filled_amount
.return_value
= portfolio
.Amount("BTC", "4")
1632 compute_value
.return_value
= D("125")
1634 value_from
= portfolio
.Amount("BTC", "1")
1635 value_from
.rate
= D("0.01")
1636 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1637 value_to
= portfolio
.Amount("BTC", "10")
1638 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1640 trade
.prepare_order(compute_value
="foo")
1642 filled_amount
.assert_called_with(in_base_currency
=False)
1643 compute_value
.assert_called_with(get_ticker
.return_value
["original"], "sell", compute_value
="foo")
1644 self
.assertEqual(1, len(trade
.orders
))
1645 Order
.assert_called_with("sell", portfolio
.Amount("BTC", D("5")),
1646 D("125"), "FOO", "long", "market",
1647 trade
, close_if_possible
=False)
1650 @mock.patch.object(portfolio
.Trade
, "prepare_order")
1651 def test_update_order(self
, prepare_order
):
1652 order_mock
= mock
.Mock()
1653 new_order_mock
= mock
.Mock()
1655 value_from
= portfolio
.Amount("BTC", "0.5")
1656 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1657 value_to
= portfolio
.Amount("BTC", "1.0")
1658 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1659 prepare_order
.return_value
= new_order_mock
1661 for i
in [0, 1, 3, 4, 6]:
1662 with self
.subTest(tick
=i
),\
1663 mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1664 trade
.update_order(order_mock
, i
)
1665 order_mock
.cancel
.assert_not_called()
1666 new_order_mock
.run
.assert_not_called()
1667 log_order
.assert_called_once_with(order_mock
, i
,
1668 update
="waiting", compute_value
=None, new_order
=None)
1670 order_mock
.reset_mock()
1671 new_order_mock
.reset_mock()
1674 with mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1675 trade
.update_order(order_mock
, 2)
1676 order_mock
.cancel
.assert_called()
1677 new_order_mock
.run
.assert_called()
1678 prepare_order
.assert_called()
1679 log_order
.assert_called()
1680 self
.assertEqual(2, log_order
.call_count
)
1682 mock
.call(order_mock
, 2, update
="adjusting",
1683 compute_value
='lambda x, y: (x[y] + x["average"]) / 2',
1684 new_order
=new_order_mock
),
1685 mock
.call(order_mock
, 2, new_order
=new_order_mock
),
1687 log_order
.assert_has_calls(calls
)
1689 order_mock
.reset_mock()
1690 new_order_mock
.reset_mock()
1693 with mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1694 trade
.update_order(order_mock
, 5)
1695 order_mock
.cancel
.assert_called()
1696 new_order_mock
.run
.assert_called()
1697 prepare_order
.assert_called()
1698 self
.assertEqual(2, log_order
.call_count
)
1699 log_order
.assert_called()
1701 mock
.call(order_mock
, 5, update
="adjusting",
1702 compute_value
='lambda x, y: (x[y]*2 + x["average"]) / 3',
1703 new_order
=new_order_mock
),
1704 mock
.call(order_mock
, 5, new_order
=new_order_mock
),
1706 log_order
.assert_has_calls(calls
)
1708 order_mock
.reset_mock()
1709 new_order_mock
.reset_mock()
1712 with mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1713 trade
.update_order(order_mock
, 7)
1714 order_mock
.cancel
.assert_called()
1715 new_order_mock
.run
.assert_called()
1716 prepare_order
.assert_called_with(compute_value
="default")
1717 log_order
.assert_called()
1718 self
.assertEqual(2, log_order
.call_count
)
1720 mock
.call(order_mock
, 7, update
="market_fallback",
1721 compute_value
='default',
1722 new_order
=new_order_mock
),
1723 mock
.call(order_mock
, 7, new_order
=new_order_mock
),
1725 log_order
.assert_has_calls(calls
)
1727 order_mock
.reset_mock()
1728 new_order_mock
.reset_mock()
1731 for i
in [10, 13, 16]:
1732 with self
.subTest(tick
=i
), mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1733 trade
.update_order(order_mock
, i
)
1734 order_mock
.cancel
.assert_called()
1735 new_order_mock
.run
.assert_called()
1736 prepare_order
.assert_called_with(compute_value
="default")
1737 log_order
.assert_called()
1738 self
.assertEqual(2, log_order
.call_count
)
1740 mock
.call(order_mock
, i
, update
="market_adjust",
1741 compute_value
='default',
1742 new_order
=new_order_mock
),
1743 mock
.call(order_mock
, i
, new_order
=new_order_mock
),
1745 log_order
.assert_has_calls(calls
)
1747 order_mock
.reset_mock()
1748 new_order_mock
.reset_mock()
1751 for i
in [8, 9, 11, 12]:
1752 with self
.subTest(tick
=i
), mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1753 trade
.update_order(order_mock
, i
)
1754 order_mock
.cancel
.assert_not_called()
1755 new_order_mock
.run
.assert_not_called()
1756 log_order
.assert_called_once_with(order_mock
, i
, update
="waiting",
1757 compute_value
=None, new_order
=None)
1759 order_mock
.reset_mock()
1760 new_order_mock
.reset_mock()
1764 @mock.patch.object(portfolio
.ReportStore
, "print_log")
1765 def test_print_with_order(self
, print_log
):
1766 value_from
= portfolio
.Amount("BTC", "0.5")
1767 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1768 value_to
= portfolio
.Amount("BTC", "1.0")
1769 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1771 order_mock1
= mock
.Mock()
1772 order_mock1
.__repr__
= mock
.Mock()
1773 order_mock1
.__repr__
.return_value
= "Mock 1"
1774 order_mock2
= mock
.Mock()
1775 order_mock2
.__repr__
= mock
.Mock()
1776 order_mock2
.__repr__
.return_value
= "Mock 2"
1777 order_mock1
.mouvements
= []
1778 mouvement_mock1
= mock
.Mock()
1779 mouvement_mock1
.__repr__
= mock
.Mock()
1780 mouvement_mock1
.__repr__
.return_value
= "Mouvement 1"
1781 mouvement_mock2
= mock
.Mock()
1782 mouvement_mock2
.__repr__
= mock
.Mock()
1783 mouvement_mock2
.__repr__
.return_value
= "Mouvement 2"
1784 order_mock2
.mouvements
= [
1785 mouvement_mock1
, mouvement_mock2
1787 trade
.orders
.append(order_mock1
)
1788 trade
.orders
.append(order_mock2
)
1790 trade
.print_with_order()
1792 print_log
.assert_called()
1793 calls
= print_log
.mock_calls
1794 self
.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(calls
[0][1][0]))
1795 self
.assertEqual("\tMock 1", str(calls
[1][1][0]))
1796 self
.assertEqual("\tMock 2", str(calls
[2][1][0]))
1797 self
.assertEqual("\t\tMouvement 1", str(calls
[3][1][0]))
1798 self
.assertEqual("\t\tMouvement 2", str(calls
[4][1][0]))
1800 def test__repr(self
):
1801 value_from
= portfolio
.Amount("BTC", "0.5")
1802 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1803 value_to
= portfolio
.Amount("BTC", "1.0")
1804 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1806 self
.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade
))
1808 def test_as_json(self
):
1809 value_from
= portfolio
.Amount("BTC", "0.5")
1810 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1811 value_to
= portfolio
.Amount("BTC", "1.0")
1812 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1814 as_json
= trade
.as_json()
1815 self
.assertEqual("acquire", as_json
["action"])
1816 self
.assertEqual(D("0.5"), as_json
["from"])
1817 self
.assertEqual(D("1.0"), as_json
["to"])
1818 self
.assertEqual("ETH", as_json
["currency"])
1819 self
.assertEqual("BTC", as_json
["base_currency"])
1821 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1822 class OrderTest(WebMockTestCase
):
1823 def test_values(self
):
1824 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1825 D("0.1"), "BTC", "long", "market", "trade")
1826 self
.assertEqual("buy", order
.action
)
1827 self
.assertEqual(10, order
.amount
.value
)
1828 self
.assertEqual("ETH", order
.amount
.currency
)
1829 self
.assertEqual(D("0.1"), order
.rate
)
1830 self
.assertEqual("BTC", order
.base_currency
)
1831 self
.assertEqual("market", order
.market
)
1832 self
.assertEqual("long", order
.trade_type
)
1833 self
.assertEqual("pending", order
.status
)
1834 self
.assertEqual("trade", order
.trade
)
1835 self
.assertIsNone(order
.id)
1836 self
.assertFalse(order
.close_if_possible
)
1838 def test__repr(self
):
1839 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1840 D("0.1"), "BTC", "long", "market", "trade")
1841 self
.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending])", repr(order
))
1843 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1844 D("0.1"), "BTC", "long", "market", "trade",
1845 close_if_possible
=True)
1846 self
.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending] ✂)", repr(order
))
1848 def test_as_json(self
):
1849 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1850 D("0.1"), "BTC", "long", "market", "trade")
1851 mouvement_mock1
= mock
.Mock()
1852 mouvement_mock1
.as_json
.return_value
= 1
1853 mouvement_mock2
= mock
.Mock()
1854 mouvement_mock2
.as_json
.return_value
= 2
1856 order
.mouvements
= [mouvement_mock1
, mouvement_mock2
]
1857 as_json
= order
.as_json()
1858 self
.assertEqual("buy", as_json
["action"])
1859 self
.assertEqual("long", as_json
["trade_type"])
1860 self
.assertEqual(10, as_json
["amount"])
1861 self
.assertEqual("ETH", as_json
["currency"])
1862 self
.assertEqual("BTC", as_json
["base_currency"])
1863 self
.assertEqual(D("0.1"), as_json
["rate"])
1864 self
.assertEqual("pending", as_json
["status"])
1865 self
.assertEqual(False, as_json
["close_if_possible"])
1866 self
.assertIsNone(as_json
["id"])
1867 self
.assertEqual([1, 2], as_json
["mouvements"])
1869 def test_account(self
):
1870 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1871 D("0.1"), "BTC", "long", "market", "trade")
1872 self
.assertEqual("exchange", order
.account
)
1874 order
= portfolio
.Order("sell", portfolio
.Amount("ETH", 10),
1875 D("0.1"), "BTC", "short", "market", "trade")
1876 self
.assertEqual("margin", order
.account
)
1878 def test_pending(self
):
1879 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1880 D("0.1"), "BTC", "long", "market", "trade")
1881 self
.assertTrue(order
.pending
)
1882 order
.status
= "open"
1883 self
.assertFalse(order
.pending
)
1885 def test_open(self
):
1886 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1887 D("0.1"), "BTC", "long", "market", "trade")
1888 self
.assertFalse(order
.open)
1889 order
.status
= "open"
1890 self
.assertTrue(order
.open)
1892 def test_finished(self
):
1893 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1894 D("0.1"), "BTC", "long", "market", "trade")
1895 self
.assertFalse(order
.finished
)
1896 order
.status
= "closed"
1897 self
.assertTrue(order
.finished
)
1898 order
.status
= "canceled"
1899 self
.assertTrue(order
.finished
)
1900 order
.status
= "error"
1901 self
.assertTrue(order
.finished
)
1903 @mock.patch.object(portfolio
.Order
, "fetch")
1904 @mock.patch("portfolio.ReportStore")
1905 def test_cancel(self
, report_store
, fetch
):
1906 market
= mock
.Mock()
1907 portfolio
.TradeStore
.debug
= True
1908 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1909 D("0.1"), "BTC", "long", market
, "trade")
1910 order
.status
= "open"
1913 market
.cancel_order
.assert_not_called()
1914 report_store
.log_debug_action
.assert_called_once()
1915 report_store
.log_debug_action
.reset_mock()
1916 self
.assertEqual("canceled", order
.status
)
1918 portfolio
.TradeStore
.debug
= False
1919 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1920 D("0.1"), "BTC", "long", market
, "trade")
1921 order
.status
= "open"
1925 market
.cancel_order
.assert_called_with(42)
1926 fetch
.assert_called_once()
1927 report_store
.log_debug_action
.assert_not_called()
1929 def test_dust_amount_remaining(self
):
1930 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1931 D("0.1"), "BTC", "long", "market", "trade")
1932 order
.remaining_amount
= mock
.Mock(return_value
=portfolio
.Amount("ETH", 1))
1933 self
.assertFalse(order
.dust_amount_remaining())
1935 order
.remaining_amount
= mock
.Mock(return_value
=portfolio
.Amount("ETH", D("0.0001")))
1936 self
.assertTrue(order
.dust_amount_remaining())
1938 @mock.patch.object(portfolio
.Order
, "fetch")
1939 @mock.patch.object(portfolio
.Order
, "filled_amount", return_value
=portfolio
.Amount("ETH", 1))
1940 def test_remaining_amount(self
, filled_amount
, fetch
):
1941 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1942 D("0.1"), "BTC", "long", "market", "trade")
1944 self
.assertEqual(9, order
.remaining_amount().value
)
1945 order
.fetch
.assert_not_called()
1947 order
.status
= "open"
1948 self
.assertEqual(9, order
.remaining_amount().value
)
1949 fetch
.assert_called_once()
1951 @mock.patch.object(portfolio
.Order
, "fetch")
1952 def test_filled_amount(self
, fetch
):
1953 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1954 D("0.1"), "BTC", "long", "market", "trade")
1955 order
.mouvements
.append(portfolio
.Mouvement("ETH", "BTC", {
1956 "tradeID": 42, "type": "buy", "fee": "0.0015",
1957 "date": "2017-12-30 12:00:12", "rate": "0.1",
1958 "amount": "3", "total": "0.3"
1960 order
.mouvements
.append(portfolio
.Mouvement("ETH", "BTC", {
1961 "tradeID": 43, "type": "buy", "fee": "0.0015",
1962 "date": "2017-12-30 13:00:12", "rate": "0.2",
1963 "amount": "2", "total": "0.4"
1965 self
.assertEqual(portfolio
.Amount("ETH", 5), order
.filled_amount())
1966 fetch
.assert_not_called()
1967 order
.status
= "open"
1968 self
.assertEqual(portfolio
.Amount("ETH", 5), order
.filled_amount(in_base_currency
=False))
1969 fetch
.assert_called_once()
1970 self
.assertEqual(portfolio
.Amount("BTC", "0.7"), order
.filled_amount(in_base_currency
=True))
1972 def test_fetch_mouvements(self
):
1973 market
= mock
.Mock()
1974 market
.privatePostReturnOrderTrades
.return_value
= [
1976 "tradeID": 42, "type": "buy", "fee": "0.0015",
1977 "date": "2017-12-30 12:00:12", "rate": "0.1",
1978 "amount": "3", "total": "0.3"
1981 "tradeID": 43, "type": "buy", "fee": "0.0015",
1982 "date": "2017-12-30 13:00:12", "rate": "0.2",
1983 "amount": "2", "total": "0.4"
1986 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1987 D("0.1"), "BTC", "long", market
, "trade")
1989 order
.mouvements
= ["Foo", "Bar", "Baz"]
1991 order
.fetch_mouvements()
1993 market
.privatePostReturnOrderTrades
.assert_called_with({"orderNumber": 12}
)
1994 self
.assertEqual(2, len(order
.mouvements
))
1995 self
.assertEqual(42, order
.mouvements
[0].id)
1996 self
.assertEqual(43, order
.mouvements
[1].id)
1998 market
.privatePostReturnOrderTrades
.side_effect
= portfolio
.ExchangeError
1999 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2000 D("0.1"), "BTC", "long", market
, "trade")
2001 order
.fetch_mouvements()
2002 self
.assertEqual(0, len(order
.mouvements
))
2004 @mock.patch("portfolio.ReportStore")
2005 def test_mark_finished_order(self
, report_store
):
2006 market
= mock
.Mock()
2007 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2008 D("0.1"), "BTC", "short", market
, "trade",
2009 close_if_possible
=True)
2010 order
.status
= "closed"
2011 portfolio
.TradeStore
.debug
= False
2013 order
.mark_finished_order()
2014 market
.close_margin_position
.assert_called_with("ETH", "BTC")
2015 market
.close_margin_position
.reset_mock()
2017 order
.status
= "open"
2018 order
.mark_finished_order()
2019 market
.close_margin_position
.assert_not_called()
2021 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2022 D("0.1"), "BTC", "short", market
, "trade",
2023 close_if_possible
=False)
2024 order
.status
= "closed"
2025 order
.mark_finished_order()
2026 market
.close_margin_position
.assert_not_called()
2028 order
= portfolio
.Order("sell", portfolio
.Amount("ETH", 10),
2029 D("0.1"), "BTC", "short", market
, "trade",
2030 close_if_possible
=True)
2031 order
.status
= "closed"
2032 order
.mark_finished_order()
2033 market
.close_margin_position
.assert_not_called()
2035 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2036 D("0.1"), "BTC", "long", market
, "trade",
2037 close_if_possible
=True)
2038 order
.status
= "closed"
2039 order
.mark_finished_order()
2040 market
.close_margin_position
.assert_not_called()
2042 portfolio
.TradeStore
.debug
= True
2044 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2045 D("0.1"), "BTC", "short", market
, "trade",
2046 close_if_possible
=True)
2047 order
.status
= "closed"
2049 order
.mark_finished_order()
2050 market
.close_margin_position
.assert_not_called()
2051 report_store
.log_debug_action
.assert_called_once()
2053 @mock.patch.object(portfolio
.Order
, "fetch_mouvements")
2054 @mock.patch("portfolio.ReportStore")
2055 def test_fetch(self
, report_store
, fetch_mouvements
):
2056 time
= self
.time
.time()
2057 with mock
.patch
.object(portfolio
.time
, "time") as time_mock
:
2058 market
= mock
.Mock()
2059 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2060 D("0.1"), "BTC", "long", market
, "trade")
2062 with self
.subTest(debug
=True):
2063 portfolio
.TradeStore
.debug
= True
2065 time_mock
.assert_not_called()
2066 report_store
.log_debug_action
.assert_called_once()
2067 report_store
.log_debug_action
.reset_mock()
2068 order
.fetch(force
=True)
2069 time_mock
.assert_not_called()
2070 market
.fetch_order
.assert_not_called()
2071 fetch_mouvements
.assert_not_called()
2072 report_store
.log_debug_action
.assert_called_once()
2073 report_store
.log_debug_action
.reset_mock()
2074 self
.assertIsNone(order
.fetch_cache_timestamp
)
2076 with self
.subTest(debug
=False):
2077 portfolio
.TradeStore
.debug
= False
2078 time_mock
.return_value
= time
2079 market
.fetch_order
.return_value
= {
2081 "datetime": "timestamp"
2085 market
.fetch_order
.assert_called_once()
2086 fetch_mouvements
.assert_called_once()
2087 self
.assertEqual("foo", order
.status
)
2088 self
.assertEqual("timestamp", order
.timestamp
)
2089 self
.assertEqual(time
, order
.fetch_cache_timestamp
)
2090 self
.assertEqual(1, len(order
.results
))
2092 market
.fetch_order
.reset_mock()
2093 fetch_mouvements
.reset_mock()
2095 time_mock
.return_value
= time
+ 8
2097 market
.fetch_order
.assert_not_called()
2098 fetch_mouvements
.assert_not_called()
2100 order
.fetch(force
=True)
2101 market
.fetch_order
.assert_called_once()
2102 fetch_mouvements
.assert_called_once()
2104 market
.fetch_order
.reset_mock()
2105 fetch_mouvements
.reset_mock()
2107 time_mock
.return_value
= time
+ 19
2109 market
.fetch_order
.assert_called_once()
2110 fetch_mouvements
.assert_called_once()
2111 report_store
.log_debug_action
.assert_not_called()
2113 @mock.patch.object(portfolio
.Order
, "fetch")
2114 @mock.patch.object(portfolio
.Order
, "mark_finished_order")
2115 @mock.patch("portfolio.ReportStore")
2116 def test_get_status(self
, report_store
, mark_finished_order
, fetch
):
2117 with self
.subTest(debug
=True):
2118 portfolio
.TradeStore
.debug
= True
2119 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2120 D("0.1"), "BTC", "long", "market", "trade")
2121 self
.assertEqual("pending", order
.get_status())
2122 fetch
.assert_not_called()
2123 report_store
.log_debug_action
.assert_called_once()
2125 with self
.subTest(debug
=False, finished
=False):
2126 portfolio
.TradeStore
.debug
= False
2127 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2128 D("0.1"), "BTC", "long", "market", "trade")
2130 def update_status():
2131 order
.status
= "open"
2132 return update_status
2133 fetch
.side_effect
= _fetch(order
)
2134 self
.assertEqual("open", order
.get_status())
2135 mark_finished_order
.assert_not_called()
2136 fetch
.assert_called_once()
2138 mark_finished_order
.reset_mock()
2140 with self
.subTest(debug
=False, finished
=True):
2141 portfolio
.TradeStore
.debug
= False
2142 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2143 D("0.1"), "BTC", "long", "market", "trade")
2145 def update_status():
2146 order
.status
= "closed"
2147 return update_status
2148 fetch
.side_effect
= _fetch(order
)
2149 self
.assertEqual("closed", order
.get_status())
2150 mark_finished_order
.assert_called_once()
2151 fetch
.assert_called_once()
2154 market
= mock
.Mock()
2156 market
.order_precision
.return_value
= 4
2157 with self
.subTest(debug
=True),\
2158 mock
.patch('portfolio.ReportStore') as report_store
:
2159 portfolio
.TradeStore
.debug
= True
2160 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2161 D("0.1"), "BTC", "long", market
, "trade")
2163 market
.create_order
.assert_not_called()
2164 report_store
.log_debug_action
.assert_called_with("market.create_order('ETH/BTC', 'limit', 'buy', 10.0000, price=0.1, account=exchange)")
2165 self
.assertEqual("open", order
.status
)
2166 self
.assertEqual(1, len(order
.results
))
2167 self
.assertEqual(-1, order
.id)
2169 market
.create_order
.reset_mock()
2170 with self
.subTest(debug
=False):
2171 portfolio
.TradeStore
.debug
= False
2172 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2173 D("0.1"), "BTC", "long", market
, "trade")
2174 market
.create_order
.return_value
= { "id": 123 }
2176 market
.create_order
.assert_called_once()
2177 self
.assertEqual(1, len(order
.results
))
2178 self
.assertEqual("open", order
.status
)
2180 market
.create_order
.reset_mock()
2181 with self
.subTest(exception
=True),\
2182 mock
.patch('portfolio.ReportStore') as report_store
:
2183 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2184 D("0.1"), "BTC", "long", market
, "trade")
2185 market
.create_order
.side_effect
= Exception("bouh")
2187 market
.create_order
.assert_called_once()
2188 self
.assertEqual(0, len(order
.results
))
2189 self
.assertEqual("error", order
.status
)
2190 report_store
.log_error
.assert_called_once()
2192 market
.create_order
.reset_mock()
2193 with self
.subTest(dust_amount_exception
=True),\
2194 mock
.patch
.object(portfolio
.Order
, "mark_finished_order") as mark_finished_order
:
2195 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 0.001),
2196 D("0.1"), "BTC", "long", market
, "trade")
2197 market
.create_order
.side_effect
= portfolio
.ExchangeNotAvailable
2199 market
.create_order
.assert_called_once()
2200 self
.assertEqual(0, len(order
.results
))
2201 self
.assertEqual("closed", order
.status
)
2202 mark_finished_order
.assert_called_once()
2205 @unittest.skipUnless("unit" in limits
, "Unit skipped")
2206 class MouvementTest(WebMockTestCase
):
2207 def test_values(self
):
2208 mouvement
= portfolio
.Mouvement("ETH", "BTC", {
2209 "tradeID": 42, "type": "buy", "fee": "0.0015",
2210 "date": "2017-12-30 12:00:12", "rate": "0.1",
2211 "amount": "10", "total": "1"
2213 self
.assertEqual("ETH", mouvement
.currency
)
2214 self
.assertEqual("BTC", mouvement
.base_currency
)
2215 self
.assertEqual(42, mouvement
.id)
2216 self
.assertEqual("buy", mouvement
.action
)
2217 self
.assertEqual(D("0.0015"), mouvement
.fee_rate
)
2218 self
.assertEqual(portfolio
.datetime(2017, 12, 30, 12, 0, 12), mouvement
.date
)
2219 self
.assertEqual(D("0.1"), mouvement
.rate
)
2220 self
.assertEqual(portfolio
.Amount("ETH", "10"), mouvement
.total
)
2221 self
.assertEqual(portfolio
.Amount("BTC", "1"), mouvement
.total_in_base
)
2223 mouvement
= portfolio
.Mouvement("ETH", "BTC", { "foo": "bar" }
)
2224 self
.assertIsNone(mouvement
.date
)
2225 self
.assertIsNone(mouvement
.id)
2226 self
.assertIsNone(mouvement
.action
)
2227 self
.assertEqual(-1, mouvement
.fee_rate
)
2228 self
.assertEqual(0, mouvement
.rate
)
2229 self
.assertEqual(portfolio
.Amount("ETH", 0), mouvement
.total
)
2230 self
.assertEqual(portfolio
.Amount("BTC", 0), mouvement
.total_in_base
)
2232 def test__repr(self
):
2233 mouvement
= portfolio
.Mouvement("ETH", "BTC", {
2234 "tradeID": 42, "type": "buy", "fee": "0.0015",
2235 "date": "2017-12-30 12:00:12", "rate": "0.1",
2236 "amount": "10", "total": "1"
2238 self
.assertEqual("Mouvement(2017-12-30 12:00:12 ; buy 10.00000000 ETH (1.00000000 BTC) fee: 0.1500%)", repr(mouvement
))
2240 mouvement
= portfolio
.Mouvement("ETH", "BTC", {
2241 "tradeID": 42, "type": "buy",
2242 "date": "garbage", "rate": "0.1",
2243 "amount": "10", "total": "1"
2245 self
.assertEqual("Mouvement(No date ; buy 10.00000000 ETH (1.00000000 BTC))", repr(mouvement
))
2247 def test_as_json(self
):
2248 mouvement
= portfolio
.Mouvement("ETH", "BTC", {
2249 "tradeID": 42, "type": "buy", "fee": "0.0015",
2250 "date": "2017-12-30 12:00:12", "rate": "0.1",
2251 "amount": "10", "total": "1"
2253 as_json
= mouvement
.as_json()
2255 self
.assertEqual(D("0.0015"), as_json
["fee_rate"])
2256 self
.assertEqual(portfolio
.datetime(2017, 12, 30, 12, 0, 12), as_json
["date"])
2257 self
.assertEqual("buy", as_json
["action"])
2258 self
.assertEqual(D("10"), as_json
["total"])
2259 self
.assertEqual(D("1"), as_json
["total_in_base"])
2260 self
.assertEqual("BTC", as_json
["base_currency"])
2261 self
.assertEqual("ETH", as_json
["currency"])
2263 @unittest.skipUnless("unit" in limits
, "Unit skipped")
2264 class ReportStoreTest(WebMockTestCase
):
2265 def test_add_log(self
):
2266 portfolio
.ReportStore
.add_log({"foo": "bar"}
)
2268 self
.assertEqual({"foo": "bar", "date": mock.ANY}
, portfolio
.ReportStore
.logs
[0])
2270 def test_set_verbose(self
):
2271 with self
.subTest(verbose
=True):
2272 portfolio
.ReportStore
.set_verbose(True)
2273 self
.assertTrue(portfolio
.ReportStore
.verbose_print
)
2275 with self
.subTest(verbose
=False):
2276 portfolio
.ReportStore
.set_verbose(False)
2277 self
.assertFalse(portfolio
.ReportStore
.verbose_print
)
2279 def test_print_log(self
):
2280 with self
.subTest(verbose
=True),\
2281 mock
.patch('sys.stdout', new_callable
=StringIO
) as stdout_mock
:
2282 portfolio
.ReportStore
.set_verbose(True)
2283 portfolio
.ReportStore
.print_log("Coucou")
2284 portfolio
.ReportStore
.print_log(portfolio
.Amount("BTC", 1))
2285 self
.assertEqual(stdout_mock
.getvalue(), "Coucou\n1.00000000 BTC\n")
2287 with self
.subTest(verbose
=False),\
2288 mock
.patch('sys.stdout', new_callable
=StringIO
) as stdout_mock
:
2289 portfolio
.ReportStore
.set_verbose(False)
2290 portfolio
.ReportStore
.print_log("Coucou")
2291 portfolio
.ReportStore
.print_log(portfolio
.Amount("BTC", 1))
2292 self
.assertEqual(stdout_mock
.getvalue(), "")
2294 def test_to_json(self
):
2295 portfolio
.ReportStore
.logs
.append({"foo": "bar"}
)
2296 self
.assertEqual('[{"foo": "bar"}]', portfolio
.ReportStore
.to_json())
2297 portfolio
.ReportStore
.logs
.append({"date": portfolio.datetime(2018, 2, 24)}
)
2298 self
.assertEqual('[{"foo": "bar"}, {"date": "2018-02-24T00:00:00"}]', portfolio
.ReportStore
.to_json())
2299 portfolio
.ReportStore
.logs
.append({"amount": portfolio.Amount("BTC", 1)}
)
2300 with self
.assertRaises(TypeError):
2301 portfolio
.ReportStore
.to_json()
2303 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2304 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2305 def test_log_stage(self
, add_log
, print_log
):
2306 portfolio
.ReportStore
.log_stage("foo")
2307 print_log
.assert_has_calls([
2308 mock
.call("-----------"),
2309 mock
.call("[Stage] foo"),
2311 add_log
.assert_called_once_with({'type': 'stage', 'stage': 'foo'}
)
2313 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2314 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2315 @mock.patch("store.BalanceStore")
2316 def test_log_balances(self
, balance_store
, add_log
, print_log
):
2317 balance_store
.as_json
.return_value
= "json"
2318 balance_store
.all
= { "FOO": "bar", "BAR": "baz" }
2320 portfolio
.ReportStore
.log_balances("market")
2321 print_log
.assert_has_calls([
2322 mock
.call("[Balance]"),
2326 add_log
.assert_called_once_with({'type': 'balance', 'balances': 'json'}
)
2328 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2329 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2330 def test_log_tickers(self
, add_log
, print_log
):
2331 market
= mock
.Mock()
2333 "BTC": portfolio
.Amount("BTC", 10),
2334 "ETH": portfolio
.Amount("BTC", D("0.3"))
2336 amounts
["ETH"].rate
= D("0.1")
2338 portfolio
.ReportStore
.log_tickers(market
, amounts
, "BTC", "default", "total")
2339 print_log
.assert_not_called()
2340 add_log
.assert_called_once_with({
2342 'compute_value': 'default',
2343 'balance_type': 'total',
2356 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2357 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2358 def test_log_dispatch(self
, add_log
, print_log
):
2359 amount
= portfolio
.Amount("BTC", "10.3")
2361 "BTC": portfolio
.Amount("BTC", 10),
2362 "ETH": portfolio
.Amount("BTC", D("0.3"))
2364 portfolio
.ReportStore
.log_dispatch(amount
, amounts
, "medium", "repartition")
2365 print_log
.assert_not_called()
2366 add_log
.assert_called_once_with({
2368 'liquidity': 'medium',
2369 'repartition_ratio': 'repartition',
2380 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2381 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2382 def test_log_trades(self
, add_log
, print_log
):
2383 trade_mock1
= mock
.Mock()
2384 trade_mock2
= mock
.Mock()
2385 trade_mock1
.as_json
.return_value
= { "trade": "1" }
2386 trade_mock2
.as_json
.return_value
= { "trade": "2" }
2388 matching_and_trades
= [
2389 (True, trade_mock1
),
2390 (False, trade_mock2
),
2392 portfolio
.ReportStore
.log_trades(matching_and_trades
, "only", "debug")
2394 print_log
.assert_not_called()
2395 add_log
.assert_called_with({
2400 {'trade': '1', 'skipped': False}
,
2401 {'trade': '2', 'skipped': True}
2405 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2406 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2407 @mock.patch.object(portfolio
.TradeStore
, "print_all_with_order")
2408 def test_log_orders(self
, print_all_with_order
, add_log
, print_log
):
2409 order_mock1
= mock
.Mock()
2410 order_mock2
= mock
.Mock()
2412 order_mock1
.as_json
.return_value
= "order1"
2413 order_mock2
.as_json
.return_value
= "order2"
2415 orders
= [order_mock1
, order_mock2
]
2417 portfolio
.ReportStore
.log_orders(orders
, tick
="tick",
2418 only
="only", compute_value
="compute_value")
2420 print_log
.assert_called_once_with("[Orders]")
2421 print_all_with_order
.assert_called_once_with(ind
="\t")
2423 add_log
.assert_called_with({
2426 'compute_value': 'compute_value',
2428 'orders': ['order1', 'order2']
2431 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2432 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2433 def test_log_order(self
, add_log
, print_log
):
2434 order_mock
= mock
.Mock()
2435 order_mock
.as_json
.return_value
= "order"
2436 new_order_mock
= mock
.Mock()
2437 new_order_mock
.as_json
.return_value
= "new_order"
2438 order_mock
.__repr
__ = mock
.Mock()
2439 order_mock
.__repr
__.return_value
= "Order Mock"
2440 new_order_mock
.__repr
__ = mock
.Mock()
2441 new_order_mock
.__repr
__.return_value
= "New order Mock"
2443 with self
.subTest(finished
=True):
2444 portfolio
.ReportStore
.log_order(order_mock
, 1, finished
=True)
2445 print_log
.assert_called_once_with("[Order] Finished Order Mock")
2446 add_log
.assert_called_once_with({
2451 'compute_value': None,
2455 add_log
.reset_mock()
2456 print_log
.reset_mock()
2458 with self
.subTest(update
="waiting"):
2459 portfolio
.ReportStore
.log_order(order_mock
, 1, update
="waiting")
2460 print_log
.assert_called_once_with("[Order] Order Mock, tick 1, waiting")
2461 add_log
.assert_called_once_with({
2464 'update': 'waiting',
2466 'compute_value': None,
2470 add_log
.reset_mock()
2471 print_log
.reset_mock()
2472 with self
.subTest(update
="adjusting"):
2473 portfolio
.ReportStore
.log_order(order_mock
, 3,
2474 update
="adjusting", new_order
=new_order_mock
,
2475 compute_value
="default")
2476 print_log
.assert_called_once_with("[Order] Order Mock, tick 3, cancelling and adjusting to New order Mock")
2477 add_log
.assert_called_once_with({
2480 'update': 'adjusting',
2482 'compute_value': "default",
2483 'new_order': 'new_order'
2486 add_log
.reset_mock()
2487 print_log
.reset_mock()
2488 with self
.subTest(update
="market_fallback"):
2489 portfolio
.ReportStore
.log_order(order_mock
, 7,
2490 update
="market_fallback", new_order
=new_order_mock
)
2491 print_log
.assert_called_once_with("[Order] Order Mock, tick 7, fallbacking to market value")
2492 add_log
.assert_called_once_with({
2495 'update': 'market_fallback',
2497 'compute_value': None,
2498 'new_order': 'new_order'
2501 add_log
.reset_mock()
2502 print_log
.reset_mock()
2503 with self
.subTest(update
="market_adjusting"):
2504 portfolio
.ReportStore
.log_order(order_mock
, 17,
2505 update
="market_adjust", new_order
=new_order_mock
)
2506 print_log
.assert_called_once_with("[Order] Order Mock, tick 17, market value, cancelling and adjusting to New order Mock")
2507 add_log
.assert_called_once_with({
2510 'update': 'market_adjust',
2512 'compute_value': None,
2513 'new_order': 'new_order'
2516 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2517 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2518 def test_log_move_balances(self
, add_log
, print_log
):
2520 "BTC": portfolio
.Amount("BTC", 10),
2524 "BTC": portfolio
.Amount("BTC", 3),
2527 portfolio
.ReportStore
.log_move_balances(needed
, moving
, True)
2528 print_log
.assert_not_called()
2529 add_log
.assert_called_once_with({
2530 'type': 'move_balances',
2542 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2543 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2544 def test_log_http_request(self
, add_log
, print_log
):
2545 response
= mock
.Mock()
2546 response
.status_code
= 200
2547 response
.text
= "Hey"
2549 portfolio
.ReportStore
.log_http_request("method", "url", "body",
2550 "headers", response
)
2551 print_log
.assert_not_called()
2552 add_log
.assert_called_once_with({
2553 'type': 'http_request',
2557 'headers': 'headers',
2562 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2563 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2564 def test_log_error(self
, add_log
, print_log
):
2565 with self
.subTest(message
=None, exception
=None):
2566 portfolio
.ReportStore
.log_error("action")
2567 print_log
.assert_called_once_with("[Error] action")
2568 add_log
.assert_called_once_with({
2571 'exception_class': None,
2572 'exception_message': None,
2576 print_log
.reset_mock()
2577 add_log
.reset_mock()
2578 with self
.subTest(message
="Hey", exception
=None):
2579 portfolio
.ReportStore
.log_error("action", message
="Hey")
2580 print_log
.assert_has_calls([
2581 mock
.call("[Error] action"),
2584 add_log
.assert_called_once_with({
2587 'exception_class': None,
2588 'exception_message': None,
2592 print_log
.reset_mock()
2593 add_log
.reset_mock()
2594 with self
.subTest(message
=None, exception
=Exception("bouh")):
2595 portfolio
.ReportStore
.log_error("action", exception
=Exception("bouh"))
2596 print_log
.assert_has_calls([
2597 mock
.call("[Error] action"),
2598 mock
.call("\tException: bouh")
2600 add_log
.assert_called_once_with({
2603 'exception_class': "Exception",
2604 'exception_message': "bouh",
2608 print_log
.reset_mock()
2609 add_log
.reset_mock()
2610 with self
.subTest(message
="Hey", exception
=Exception("bouh")):
2611 portfolio
.ReportStore
.log_error("action", message
="Hey", exception
=Exception("bouh"))
2612 print_log
.assert_has_calls([
2613 mock
.call("[Error] action"),
2614 mock
.call("\tException: bouh"),
2617 add_log
.assert_called_once_with({
2620 'exception_class': "Exception",
2621 'exception_message': "bouh",
2625 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2626 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2627 def test_log_debug_action(self
, add_log
, print_log
):
2628 portfolio
.ReportStore
.log_debug_action("Hey")
2630 print_log
.assert_called_once_with("[Debug] Hey")
2631 add_log
.assert_called_once_with({
2632 'type': 'debug_action',
2636 @unittest.skipUnless("acceptance" in limits
, "Acceptance skipped")
2637 class AcceptanceTest(WebMockTestCase
):
2638 @unittest.expectedFailure
2639 def test_success_sell_only_necessary(self
):
2640 # FIXME: catch stdout
2641 portfolio
.ReportStore
.verbose_print
= False
2644 "exchange_free": D("1.0"),
2645 "exchange_used": D("0.0"),
2646 "exchange_total": D("1.0"),
2650 "exchange_free": D("4.0"),
2651 "exchange_used": D("0.0"),
2652 "exchange_total": D("4.0"),
2656 "exchange_free": D("1000.0"),
2657 "exchange_used": D("0.0"),
2658 "exchange_total": D("1000.0"),
2659 "total": D("1000.0"),
2663 "ETH": (D("0.25"), "long"),
2664 "ETC": (D("0.25"), "long"),
2665 "BTC": (D("0.4"), "long"),
2666 "BTD": (D("0.01"), "short"),
2667 "B2X": (D("0.04"), "long"),
2668 "USDT": (D("0.05"), "long"),
2671 def fetch_ticker(symbol
):
2672 if symbol
== "ETH/BTC":
2674 "symbol": "ETH/BTC",
2678 if symbol
== "ETC/BTC":
2680 "symbol": "ETC/BTC",
2684 if symbol
== "XVG/BTC":
2686 "symbol": "XVG/BTC",
2687 "bid": D("0.00003"),
2690 if symbol
== "BTD/BTC":
2692 "symbol": "BTD/BTC",
2696 if symbol
== "B2X/BTC":
2698 "symbol": "B2X/BTC",
2702 if symbol
== "USDT/BTC":
2703 raise helper
.ExchangeError
2704 if symbol
== "BTC/USDT":
2706 "symbol": "BTC/USDT",
2710 self
.fail("Shouldn't have been called with {}".format(symbol
))
2712 market
= mock
.Mock()
2713 market
.fetch_all_balances
.return_value
= fetch_balance
2714 market
.fetch_ticker
.side_effect
= fetch_ticker
2715 with mock
.patch
.object(portfolio
.Portfolio
, "repartition", return_value
=repartition
):
2717 helper
.prepare_trades(market
)
2719 balances
= portfolio
.BalanceStore
.all
2720 self
.assertEqual(portfolio
.Amount("ETH", 1), balances
["ETH"].total
)
2721 self
.assertEqual(portfolio
.Amount("ETC", 4), balances
["ETC"].total
)
2722 self
.assertEqual(portfolio
.Amount("XVG", 1000), balances
["XVG"].total
)
2725 trades
= portfolio
.TradeStore
.all
2726 self
.assertEqual(portfolio
.Amount("BTC", D("0.15")), trades
[0].value_from
)
2727 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
[0].value_to
)
2728 self
.assertEqual("dispose", trades
[0].action
)
2730 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
[1].value_from
)
2731 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
[1].value_to
)
2732 self
.assertEqual("acquire", trades
[1].action
)
2734 self
.assertEqual(portfolio
.Amount("BTC", D("0.04")), trades
[2].value_from
)
2735 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[2].value_to
)
2736 self
.assertEqual("dispose", trades
[2].action
)
2738 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[3].value_from
)
2739 self
.assertEqual(portfolio
.Amount("BTC", D("-0.002")), trades
[3].value_to
)
2740 self
.assertEqual("acquire", trades
[3].action
)
2742 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[4].value_from
)
2743 self
.assertEqual(portfolio
.Amount("BTC", D("0.008")), trades
[4].value_to
)
2744 self
.assertEqual("acquire", trades
[4].action
)
2746 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[5].value_from
)
2747 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
[5].value_to
)
2748 self
.assertEqual("acquire", trades
[5].action
)
2751 portfolio
.TradeStore
.prepare_orders(only
="dispose", compute_value
=lambda x
, y
: x
["bid"] * D("1.001"))
2753 all_orders
= portfolio
.TradeStore
.all_orders(state
="pending")
2754 self
.assertEqual(2, len(all_orders
))
2755 self
.assertEqual(2, 3*all_orders
[0].amount
.value
)
2756 self
.assertEqual(D("0.14014"), all_orders
[0].rate
)
2757 self
.assertEqual(1000, all_orders
[1].amount
.value
)
2758 self
.assertEqual(D("0.00003003"), all_orders
[1].rate
)
2761 def create_order(symbol
, type, action
, amount
, price
=None, account
="exchange"):
2762 self
.assertEqual("limit", type)
2763 if symbol
== "ETH/BTC":
2764 self
.assertEqual("sell", action
)
2765 self
.assertEqual(D('0.66666666'), amount
)
2766 self
.assertEqual(D("0.14014"), price
)
2767 elif symbol
== "XVG/BTC":
2768 self
.assertEqual("sell", action
)
2769 self
.assertEqual(1000, amount
)
2770 self
.assertEqual(D("0.00003003"), price
)
2772 self
.fail("I shouldn't have been called")
2777 market
.create_order
.side_effect
= create_order
2778 market
.order_precision
.return_value
= 8
2781 portfolio
.TradeStore
.run_orders()
2783 self
.assertEqual("open", all_orders
[0].status
)
2784 self
.assertEqual("open", all_orders
[1].status
)
2786 market
.fetch_order
.return_value
= { "status": "closed", "datetime": "2018-01-20 13:40:00" }
2787 market
.privatePostReturnOrderTrades
.return_value
= [
2789 "tradeID": 42, "type": "buy", "fee": "0.0015",
2790 "date": "2017-12-30 12:00:12", "rate": "0.1",
2791 "amount": "10", "total": "1"
2794 with mock
.patch
.object(portfolio
.time
, "sleep") as sleep
:
2796 helper
.follow_orders(verbose
=False)
2798 sleep
.assert_called_with(30)
2800 for order
in all_orders
:
2801 self
.assertEqual("closed", order
.status
)
2805 "exchange_free": D("1.0") / 3,
2806 "exchange_used": D("0.0"),
2807 "exchange_total": D("1.0") / 3,
2809 "total": D("1.0") / 3,
2812 "exchange_free": D("0.134"),
2813 "exchange_used": D("0.0"),
2814 "exchange_total": D("0.134"),
2816 "total": D("0.134"),
2819 "exchange_free": D("4.0"),
2820 "exchange_used": D("0.0"),
2821 "exchange_total": D("4.0"),
2826 "exchange_free": D("0.0"),
2827 "exchange_used": D("0.0"),
2828 "exchange_total": D("0.0"),
2833 market
.fetch_all_balances
.return_value
= fetch_balance
2835 with mock
.patch
.object(portfolio
.Portfolio
, "repartition", return_value
=repartition
):
2837 helper
.update_trades(market
, only
="acquire", compute_value
="average")
2839 balances
= portfolio
.BalanceStore
.all
2840 self
.assertEqual(portfolio
.Amount("ETH", 1 / D("3")), balances
["ETH"].total
)
2841 self
.assertEqual(portfolio
.Amount("ETC", 4), balances
["ETC"].total
)
2842 self
.assertEqual(portfolio
.Amount("BTC", D("0.134")), balances
["BTC"].total
)
2843 self
.assertEqual(portfolio
.Amount("XVG", 0), balances
["XVG"].total
)
2846 trades
= portfolio
.TradeStore
.all
2847 self
.assertEqual(portfolio
.Amount("BTC", D("0.15")), trades
[0].value_from
)
2848 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
[0].value_to
)
2849 self
.assertEqual("dispose", trades
[0].action
)
2851 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
[1].value_from
)
2852 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
[1].value_to
)
2853 self
.assertEqual("acquire", trades
[1].action
)
2855 self
.assertNotIn("BTC", trades
)
2857 self
.assertEqual(portfolio
.Amount("BTC", D("0.04")), trades
[2].value_from
)
2858 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[2].value_to
)
2859 self
.assertEqual("dispose", trades
[2].action
)
2861 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[3].value_from
)
2862 self
.assertEqual(portfolio
.Amount("BTC", D("-0.002")), trades
[3].value_to
)
2863 self
.assertEqual("acquire", trades
[3].action
)
2865 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[4].value_from
)
2866 self
.assertEqual(portfolio
.Amount("BTC", D("0.008")), trades
[4].value_to
)
2867 self
.assertEqual("acquire", trades
[4].action
)
2869 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[5].value_from
)
2870 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
[5].value_to
)
2871 self
.assertEqual("acquire", trades
[5].action
)
2874 portfolio
.TradeStore
.prepare_orders(only
="acquire", compute_value
=lambda x
, y
: x
["ask"])
2876 all_orders
= portfolio
.TradeStore
.all_orders(state
="pending")
2877 self
.assertEqual(4, len(all_orders
))
2878 self
.assertEqual(portfolio
.Amount("ETC", D("12.83333333")), round(all_orders
[0].amount
))
2879 self
.assertEqual(D("0.003"), all_orders
[0].rate
)
2880 self
.assertEqual("buy", all_orders
[0].action
)
2881 self
.assertEqual("long", all_orders
[0].trade_type
)
2883 self
.assertEqual(portfolio
.Amount("BTD", D("1.61666666")), round(all_orders
[1].amount
))
2884 self
.assertEqual(D("0.0012"), all_orders
[1].rate
)
2885 self
.assertEqual("sell", all_orders
[1].action
)
2886 self
.assertEqual("short", all_orders
[1].trade_type
)
2888 diff
= portfolio
.Amount("B2X", D("19.4")/3) - all_orders
[2].amount
2889 self
.assertAlmostEqual(0, diff
.value
)
2890 self
.assertEqual(D("0.0012"), all_orders
[2].rate
)
2891 self
.assertEqual("buy", all_orders
[2].action
)
2892 self
.assertEqual("long", all_orders
[2].trade_type
)
2894 self
.assertEqual(portfolio
.Amount("BTC", D("0.0097")), all_orders
[3].amount
)
2895 self
.assertEqual(D("16000"), all_orders
[3].rate
)
2896 self
.assertEqual("sell", all_orders
[3].action
)
2897 self
.assertEqual("long", all_orders
[3].trade_type
)
2901 # Move balances to margin
2905 # portfolio.TradeStore.run_orders()
2907 with mock
.patch
.object(portfolio
.time
, "sleep") as sleep
:
2909 helper
.follow_orders(verbose
=False)
2911 sleep
.assert_called_with(30)
2913 if __name__
== '__main__':