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 portfolio
.BalanceStore
.fetch_balances(market
, tag
="tag")
658 helper
.prepare_trades(market
)
659 compute_trades
.assert_called()
661 call
= compute_trades
.call_args
662 self
.assertEqual(market
, call
[1]["market"])
663 self
.assertEqual(1, call
[0][0]["USDT"].value
)
664 self
.assertEqual(D("0.01"), call
[0][0]["XVG"].value
)
665 self
.assertEqual(D("0.2525"), call
[0][1]["BTC"].value
)
666 self
.assertEqual(D("0.7575"), call
[0][1]["XEM"].value
)
667 report_store_h
.log_stage
.assert_called_once_with("prepare_trades")
668 report_store
.log_balances
.assert_called_once_with(market
, tag
="tag")
670 @mock.patch.object(portfolio
.Portfolio
, "repartition")
671 @mock.patch.object(helper
, "get_ticker")
672 @mock.patch.object(portfolio
.TradeStore
, "compute_trades")
673 @mock.patch("store.ReportStore")
674 @mock.patch("helper.ReportStore")
675 def test_update_trades(self
, report_store_h
, report_store
, compute_trades
, get_ticker
, repartition
):
676 repartition
.return_value
= {
677 "XEM": (D("0.75"), "long"),
678 "BTC": (D("0.25"), "long"),
680 def _get_ticker(c1
, c2
, market
):
681 if c1
== "USDT" and c2
== "BTC":
682 return { "average": D("0.0001") }
683 if c1
== "XVG" and c2
== "BTC":
684 return { "average": D("0.000001") }
685 if c1
== "XEM" and c2
== "BTC":
686 return { "average": D("0.001") }
687 self
.fail("Should be called with {}, {}".format(c1
, c2
))
688 get_ticker
.side_effect
= _get_ticker
691 market
.fetch_all_balances
.return_value
= {
693 "exchange_free": D("10000.0"),
694 "exchange_used": D("0.0"),
695 "exchange_total": D("10000.0"),
696 "total": D("10000.0")
699 "exchange_free": D("10000.0"),
700 "exchange_used": D("0.0"),
701 "exchange_total": D("10000.0"),
702 "total": D("10000.0")
705 portfolio
.BalanceStore
.fetch_balances(market
, tag
="tag")
707 helper
.update_trades(market
)
708 compute_trades
.assert_called()
710 call
= compute_trades
.call_args
711 self
.assertEqual(market
, call
[1]["market"])
712 self
.assertEqual(1, call
[0][0]["USDT"].value
)
713 self
.assertEqual(D("0.01"), call
[0][0]["XVG"].value
)
714 self
.assertEqual(D("0.2525"), call
[0][1]["BTC"].value
)
715 self
.assertEqual(D("0.7575"), call
[0][1]["XEM"].value
)
716 report_store_h
.log_stage
.assert_called_once_with("update_trades")
717 report_store
.log_balances
.assert_called_once_with(market
, tag
="tag")
719 @mock.patch.object(portfolio
.Portfolio
, "repartition")
720 @mock.patch.object(helper
, "get_ticker")
721 @mock.patch.object(portfolio
.TradeStore
, "compute_trades")
722 @mock.patch("store.ReportStore")
723 @mock.patch("helper.ReportStore")
724 def test_prepare_trades_to_sell_all(self
, report_store_h
, report_store
, compute_trades
, get_ticker
, repartition
):
725 def _get_ticker(c1
, c2
, market
):
726 if c1
== "USDT" and c2
== "BTC":
727 return { "average": D("0.0001") }
728 if c1
== "XVG" and c2
== "BTC":
729 return { "average": D("0.000001") }
730 self
.fail("Should be called with {}, {}".format(c1
, c2
))
731 get_ticker
.side_effect
= _get_ticker
734 market
.fetch_all_balances
.return_value
= {
736 "exchange_free": D("10000.0"),
737 "exchange_used": D("0.0"),
738 "exchange_total": D("10000.0"),
739 "total": D("10000.0")
742 "exchange_free": D("10000.0"),
743 "exchange_used": D("0.0"),
744 "exchange_total": D("10000.0"),
745 "total": D("10000.0")
748 portfolio
.BalanceStore
.fetch_balances(market
, tag
="tag")
750 helper
.prepare_trades_to_sell_all(market
)
751 repartition
.assert_not_called()
752 compute_trades
.assert_called()
754 call
= compute_trades
.call_args
755 self
.assertEqual(market
, call
[1]["market"])
756 self
.assertEqual(1, call
[0][0]["USDT"].value
)
757 self
.assertEqual(D("0.01"), call
[0][0]["XVG"].value
)
758 self
.assertEqual(D("1.01"), call
[0][1]["BTC"].value
)
759 report_store_h
.log_stage
.assert_called_once_with("prepare_trades_to_sell_all")
760 report_store
.log_balances
.assert_called_once_with(market
, tag
="tag")
762 @mock.patch.object(portfolio
.time
, "sleep")
763 @mock.patch.object(portfolio
.TradeStore
, "all_orders")
764 def test_follow_orders(self
, all_orders
, time_mock
):
765 for debug
, sleep
in [
766 (False, None), (True, None),
767 (False, 12), (True, 12)]:
768 with self
.subTest(sleep
=sleep
, debug
=debug
), \
769 mock
.patch("helper.ReportStore") as report_store
:
770 portfolio
.TradeStore
.debug
= debug
771 order_mock1
= mock
.Mock()
772 order_mock2
= mock
.Mock()
773 order_mock3
= mock
.Mock()
774 all_orders
.side_effect
= [
775 [order_mock1
, order_mock2
],
776 [order_mock1
, order_mock2
],
778 [order_mock1
, order_mock3
],
779 [order_mock1
, order_mock3
],
781 [order_mock1
, order_mock3
],
782 [order_mock1
, order_mock3
],
787 order_mock1
.get_status
.side_effect
= ["open", "open", "closed"]
788 order_mock2
.get_status
.side_effect
= ["open"]
789 order_mock3
.get_status
.side_effect
= ["open", "closed"]
791 order_mock1
.trade
= mock
.Mock()
792 order_mock2
.trade
= mock
.Mock()
793 order_mock3
.trade
= mock
.Mock()
795 helper
.follow_orders(sleep
=sleep
)
797 order_mock1
.trade
.update_order
.assert_any_call(order_mock1
, 1)
798 order_mock1
.trade
.update_order
.assert_any_call(order_mock1
, 2)
799 self
.assertEqual(2, order_mock1
.trade
.update_order
.call_count
)
800 self
.assertEqual(3, order_mock1
.get_status
.call_count
)
802 order_mock2
.trade
.update_order
.assert_any_call(order_mock2
, 1)
803 self
.assertEqual(1, order_mock2
.trade
.update_order
.call_count
)
804 self
.assertEqual(1, order_mock2
.get_status
.call_count
)
806 order_mock3
.trade
.update_order
.assert_any_call(order_mock3
, 2)
807 self
.assertEqual(1, order_mock3
.trade
.update_order
.call_count
)
808 self
.assertEqual(2, order_mock3
.get_status
.call_count
)
809 report_store
.log_stage
.assert_called()
811 mock
.call("follow_orders_begin"),
812 mock
.call("follow_orders_tick_1"),
813 mock
.call("follow_orders_tick_2"),
814 mock
.call("follow_orders_tick_3"),
815 mock
.call("follow_orders_end"),
817 report_store
.log_stage
.assert_has_calls(calls
)
818 report_store
.log_orders
.assert_called()
819 self
.assertEqual(3, report_store
.log_orders
.call_count
)
821 mock
.call([order_mock1
, order_mock2
], tick
=1),
822 mock
.call([order_mock1
, order_mock3
], tick
=2),
823 mock
.call([order_mock1
, order_mock3
], tick
=3),
825 report_store
.log_orders
.assert_has_calls(calls
)
827 mock
.call(order_mock1
, 3, finished
=True),
828 mock
.call(order_mock3
, 3, finished
=True),
830 report_store
.log_order
.assert_has_calls(calls
)
834 report_store
.log_debug_action
.assert_called_with("Set follow_orders tick to 7s")
835 time_mock
.assert_called_with(7)
837 time_mock
.assert_called_with(30)
839 time_mock
.assert_called_with(sleep
)
841 @mock.patch.object(portfolio
.BalanceStore
, "fetch_balances")
842 def test_move_balance(self
, fetch_balances
):
843 for debug
in [True, False]:
844 with self
.subTest(debug
=debug
),\
845 mock
.patch("helper.ReportStore") as report_store
:
846 value_from
= portfolio
.Amount("BTC", "1.0")
847 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
848 value_to
= portfolio
.Amount("BTC", "10.0")
849 trade1
= portfolio
.Trade(value_from
, value_to
, "ETH")
851 value_from
= portfolio
.Amount("BTC", "0.0")
852 value_from
.linked_to
= portfolio
.Amount("ETH", "0.0")
853 value_to
= portfolio
.Amount("BTC", "-3.0")
854 trade2
= portfolio
.Trade(value_from
, value_to
, "ETH")
856 value_from
= portfolio
.Amount("USDT", "0.0")
857 value_from
.linked_to
= portfolio
.Amount("XVG", "0.0")
858 value_to
= portfolio
.Amount("USDT", "-50.0")
859 trade3
= portfolio
.Trade(value_from
, value_to
, "XVG")
861 portfolio
.TradeStore
.all
= [trade1
, trade2
, trade3
]
862 balance1
= portfolio
.Balance("BTC", { "margin_free": "0" }
)
863 balance2
= portfolio
.Balance("USDT", { "margin_free": "100" }
)
864 balance3
= portfolio
.Balance("ETC", { "margin_free": "10" }
)
865 portfolio
.BalanceStore
.all
= {"BTC": balance1, "USDT": balance2, "ETC": balance3}
869 helper
.move_balances(market
, debug
=debug
)
871 fetch_balances
.assert_called_with(market
)
872 report_store
.log_move_balances
.assert_called_once()
875 report_store
.log_debug_action
.assert_called()
876 self
.assertEqual(3, report_store
.log_debug_action
.call_count
)
878 market
.transfer_balance
.assert_any_call("BTC", 3, "exchange", "margin")
879 market
.transfer_balance
.assert_any_call("USDT", 50, "margin", "exchange")
880 market
.transfer_balance
.assert_any_call("ETC", 10, "margin", "exchange")
882 @mock.patch.object(helper
, "prepare_trades")
883 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
884 @mock.patch.object(portfolio
.BalanceStore
, "fetch_balances")
885 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
886 def test_print_orders(self
, log_stage
, fetch_balances
, prepare_orders
, prepare_trades
):
888 portfolio
.BalanceStore
.all
= {
889 "BTC": portfolio
.Balance("BTC", {
891 "exchange_total":"0.65",
892 "exchange_free": "0.35",
893 "exchange_used": "0.30"}),
894 "ETH": portfolio
.Balance("ETH", {
898 "exchange_used": 0}),
901 helper
.print_orders(market
)
902 fetch_balances
.assert_called_with(market
, tag
="print_orders")
903 prepare_trades
.assert_called_with(market
, base_currency
="BTC",
904 compute_value
="average", debug
=True)
905 prepare_orders
.assert_called_with(compute_value
="average")
906 log_stage
.assert_called_with("print_orders")
908 @mock.patch.object(portfolio
.BalanceStore
, "fetch_balances")
909 @mock.patch.object(portfolio
.BalanceStore
, "in_currency")
910 @mock.patch.object(helper
.ReportStore
, "print_log")
911 def test_print_balances(self
, print_log
, in_currency
, fetch_balances
):
913 portfolio
.BalanceStore
.all
= {
914 "BTC": portfolio
.Balance("BTC", {
916 "exchange_total":"0.65",
917 "exchange_free": "0.35",
918 "exchange_used": "0.30"}),
919 "ETH": portfolio
.Balance("ETH", {
923 "exchange_used": 0}),
925 in_currency
.return_value
= {
926 "BTC": portfolio
.Amount("BTC", "0.65"),
927 "ETH": portfolio
.Amount("BTC", "0.3"),
929 helper
.print_balances(market
)
930 fetch_balances
.assert_called_with(market
)
931 print_log
.assert_has_calls([
933 mock
.call(portfolio
.Amount("BTC", "0.95")),
936 @mock.patch.object(helper
, "prepare_trades")
937 @mock.patch.object(helper
, "follow_orders")
938 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
939 @mock.patch.object(portfolio
.TradeStore
, "run_orders")
940 @mock.patch.object(portfolio
.BalanceStore
, "fetch_balances")
941 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
942 def test_process_sell_needed__1_sell(self
, log_stage
,
943 fetch_balances
, run_orders
, prepare_orders
, follow_orders
,
946 portfolio
.BalanceStore
.all
= {
947 "BTC": portfolio
.Balance("BTC", {
949 "exchange_total":"0.65",
950 "exchange_free": "0.35",
951 "exchange_used": "0.30"}),
952 "ETH": portfolio
.Balance("ETH", {
956 "exchange_used": 0}),
958 helper
.process_sell_needed__1_sell(market
)
959 fetch_balances
.assert_has_calls([
960 mock
.call(market
, tag
="process_sell_needed__1_sell_begin"),
961 mock
.call(market
, tag
="process_sell_needed__1_sell_end"),
963 prepare_trades
.assert_called_with(market
, base_currency
="BTC",
964 liquidity
="medium", debug
=False)
965 prepare_orders
.assert_called_with(compute_value
="average",
967 run_orders
.assert_called()
968 follow_orders
.assert_called()
969 log_stage
.assert_called_with("process_sell_needed__1_sell_end")
971 @mock.patch.object(helper
, "update_trades")
972 @mock.patch.object(helper
, "follow_orders")
973 @mock.patch.object(helper
, "move_balances")
974 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
975 @mock.patch.object(portfolio
.TradeStore
, "run_orders")
976 @mock.patch.object(portfolio
.BalanceStore
, "fetch_balances")
977 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
978 def test_process_sell_needed__2_buy(self
, log_stage
, fetch_balances
,
979 run_orders
, prepare_orders
, move_balances
, follow_orders
,
982 portfolio
.BalanceStore
.all
= {
983 "BTC": portfolio
.Balance("BTC", {
985 "exchange_total":"0.65",
986 "exchange_free": "0.35",
987 "exchange_used": "0.30"}),
988 "ETH": portfolio
.Balance("ETH", {
992 "exchange_used": 0}),
994 helper
.process_sell_needed__2_buy(market
)
995 fetch_balances
.assert_has_calls([
996 mock
.call(market
, tag
="process_sell_needed__2_buy_begin"),
997 mock
.call(market
, tag
="process_sell_needed__2_buy_end"),
999 update_trades
.assert_called_with(market
, base_currency
="BTC",
1000 debug
=False, liquidity
="medium", only
="acquire")
1001 prepare_orders
.assert_called_with(compute_value
="average",
1003 move_balances
.assert_called_with(market
, debug
=False)
1004 run_orders
.assert_called()
1005 follow_orders
.assert_called()
1006 log_stage
.assert_called_with("process_sell_needed__2_buy_end")
1008 @mock.patch.object(helper
, "prepare_trades_to_sell_all")
1009 @mock.patch.object(helper
, "follow_orders")
1010 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
1011 @mock.patch.object(portfolio
.TradeStore
, "run_orders")
1012 @mock.patch.object(portfolio
.BalanceStore
, "fetch_balances")
1013 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
1014 def test_process_sell_all__1_sell(self
, log_stage
, fetch_balances
,
1015 run_orders
, prepare_orders
, follow_orders
,
1016 prepare_trades_to_sell_all
):
1017 market
= mock
.Mock()
1018 portfolio
.BalanceStore
.all
= {
1019 "BTC": portfolio
.Balance("BTC", {
1021 "exchange_total":"0.65",
1022 "exchange_free": "0.35",
1023 "exchange_used": "0.30"}),
1024 "ETH": portfolio
.Balance("ETH", {
1026 "exchange_total": 3,
1028 "exchange_used": 0}),
1030 helper
.process_sell_all__1_all_sell(market
)
1031 fetch_balances
.assert_has_calls([
1032 mock
.call(market
, tag
="process_sell_all__1_all_sell_begin"),
1033 mock
.call(market
, tag
="process_sell_all__1_all_sell_end"),
1035 prepare_trades_to_sell_all
.assert_called_with(market
, base_currency
="BTC",
1037 prepare_orders
.assert_called_with(compute_value
="average")
1038 run_orders
.assert_called()
1039 follow_orders
.assert_called()
1040 log_stage
.assert_called_with("process_sell_all__1_all_sell_end")
1042 @mock.patch.object(helper
, "prepare_trades")
1043 @mock.patch.object(helper
, "follow_orders")
1044 @mock.patch.object(helper
, "move_balances")
1045 @mock.patch.object(portfolio
.TradeStore
, "prepare_orders")
1046 @mock.patch.object(portfolio
.TradeStore
, "run_orders")
1047 @mock.patch.object(portfolio
.BalanceStore
, "fetch_balances")
1048 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
1049 def test_process_sell_all__2_all_buy(self
, log_stage
,
1050 fetch_balances
, run_orders
, prepare_orders
, move_balances
,
1051 follow_orders
, prepare_trades
):
1052 market
= mock
.Mock()
1053 portfolio
.BalanceStore
.all
= {
1054 "BTC": portfolio
.Balance("BTC", {
1056 "exchange_total":"0.65",
1057 "exchange_free": "0.35",
1058 "exchange_used": "0.30"}),
1059 "ETH": portfolio
.Balance("ETH", {
1061 "exchange_total": 3,
1063 "exchange_used": 0}),
1065 helper
.process_sell_all__2_all_buy(market
)
1066 fetch_balances
.assert_has_calls([
1067 mock
.call(market
, tag
="process_sell_all__2_all_buy_begin"),
1068 mock
.call(market
, tag
="process_sell_all__2_all_buy_end"),
1070 prepare_trades
.assert_called_with(market
, base_currency
="BTC",
1071 liquidity
="medium", debug
=False)
1072 prepare_orders
.assert_called_with(compute_value
="average")
1073 move_balances
.assert_called_with(market
, debug
=False)
1074 run_orders
.assert_called()
1075 follow_orders
.assert_called()
1076 log_stage
.assert_called_with("process_sell_all__2_all_buy_end")
1079 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1080 class TradeStoreTest(WebMockTestCase
):
1081 @mock.patch.object(portfolio
.BalanceStore
, "currencies")
1082 @mock.patch.object(portfolio
.TradeStore
, "trade_if_matching")
1083 @mock.patch.object(portfolio
.ReportStore
, "log_trades")
1084 def test_compute_trades(self
, log_trades
, trade_if_matching
, currencies
):
1085 currencies
.return_value
= ["XMR", "DASH", "XVG", "BTC", "ETH"]
1088 "XMR": portfolio
.Amount("BTC", D("0.9")),
1089 "DASH": portfolio
.Amount("BTC", D("0.4")),
1090 "XVG": portfolio
.Amount("BTC", D("-0.5")),
1091 "BTC": portfolio
.Amount("BTC", D("0.5")),
1094 "DASH": portfolio
.Amount("BTC", D("0.5")),
1095 "XVG": portfolio
.Amount("BTC", D("0.1")),
1096 "BTC": portfolio
.Amount("BTC", D("0.4")),
1097 "ETH": portfolio
.Amount("BTC", D("0.3")),
1106 trade_if_matching
.side_effect
= side_effect
1108 portfolio
.TradeStore
.compute_trades(values_in_base
,
1109 new_repartition
, only
="only", market
="market")
1111 self
.assertEqual(5, trade_if_matching
.call_count
)
1112 self
.assertEqual(3, len(portfolio
.TradeStore
.all
))
1113 self
.assertEqual([1, 4, 5], portfolio
.TradeStore
.all
)
1114 log_trades
.assert_called_with(side_effect
, "only", False)
1116 def test_trade_if_matching(self
):
1117 result
= portfolio
.TradeStore
.trade_if_matching(
1118 portfolio
.Amount("BTC", D("0")),
1119 portfolio
.Amount("BTC", D("0.3")),
1120 "ETH", only
="nope", market
="market"
1122 self
.assertEqual(False, result
[0])
1123 self
.assertIsInstance(result
[1], portfolio
.Trade
)
1125 portfolio
.TradeStore
.all
= []
1126 result
= portfolio
.TradeStore
.trade_if_matching(
1127 portfolio
.Amount("BTC", D("0")),
1128 portfolio
.Amount("BTC", D("0.3")),
1129 "ETH", only
=None, market
="market"
1131 self
.assertEqual(True, result
[0])
1133 portfolio
.TradeStore
.all
= []
1134 result
= portfolio
.TradeStore
.trade_if_matching(
1135 portfolio
.Amount("BTC", D("0")),
1136 portfolio
.Amount("BTC", D("0.3")),
1137 "ETH", only
="acquire", market
="market"
1139 self
.assertEqual(True, result
[0])
1141 portfolio
.TradeStore
.all
= []
1142 result
= portfolio
.TradeStore
.trade_if_matching(
1143 portfolio
.Amount("BTC", D("0")),
1144 portfolio
.Amount("BTC", D("0.3")),
1145 "ETH", only
="dispose", market
="market"
1147 self
.assertEqual(False, result
[0])
1149 @mock.patch.object(portfolio
.ReportStore
, "log_orders")
1150 def test_prepare_orders(self
, log_orders
):
1151 trade_mock1
= mock
.Mock()
1152 trade_mock2
= mock
.Mock()
1154 trade_mock1
.prepare_order
.return_value
= 1
1155 trade_mock2
.prepare_order
.return_value
= 2
1157 portfolio
.TradeStore
.all
.append(trade_mock1
)
1158 portfolio
.TradeStore
.all
.append(trade_mock2
)
1160 portfolio
.TradeStore
.prepare_orders()
1161 trade_mock1
.prepare_order
.assert_called_with(compute_value
="default")
1162 trade_mock2
.prepare_order
.assert_called_with(compute_value
="default")
1163 log_orders
.assert_called_once_with([1, 2], None, "default")
1165 log_orders
.reset_mock()
1167 portfolio
.TradeStore
.prepare_orders(compute_value
="bla")
1168 trade_mock1
.prepare_order
.assert_called_with(compute_value
="bla")
1169 trade_mock2
.prepare_order
.assert_called_with(compute_value
="bla")
1170 log_orders
.assert_called_once_with([1, 2], None, "bla")
1172 trade_mock1
.prepare_order
.reset_mock()
1173 trade_mock2
.prepare_order
.reset_mock()
1174 log_orders
.reset_mock()
1176 trade_mock1
.action
= "foo"
1177 trade_mock2
.action
= "bar"
1178 portfolio
.TradeStore
.prepare_orders(only
="bar")
1179 trade_mock1
.prepare_order
.assert_not_called()
1180 trade_mock2
.prepare_order
.assert_called_with(compute_value
="default")
1181 log_orders
.assert_called_once_with([2], "bar", "default")
1183 def test_print_all_with_order(self
):
1184 trade_mock1
= mock
.Mock()
1185 trade_mock2
= mock
.Mock()
1186 trade_mock3
= mock
.Mock()
1187 portfolio
.TradeStore
.all
= [trade_mock1
, trade_mock2
, trade_mock3
]
1189 portfolio
.TradeStore
.print_all_with_order()
1191 trade_mock1
.print_with_order
.assert_called()
1192 trade_mock2
.print_with_order
.assert_called()
1193 trade_mock3
.print_with_order
.assert_called()
1195 @mock.patch.object(portfolio
.ReportStore
, "log_stage")
1196 @mock.patch.object(portfolio
.ReportStore
, "log_orders")
1197 @mock.patch.object(portfolio
.TradeStore
, "all_orders")
1198 def test_run_orders(self
, all_orders
, log_orders
, log_stage
):
1199 order_mock1
= mock
.Mock()
1200 order_mock2
= mock
.Mock()
1201 order_mock3
= mock
.Mock()
1202 all_orders
.return_value
= [order_mock1
, order_mock2
, order_mock3
]
1203 portfolio
.TradeStore
.run_orders()
1204 all_orders
.assert_called_with(state
="pending")
1206 order_mock1
.run
.assert_called()
1207 order_mock2
.run
.assert_called()
1208 order_mock3
.run
.assert_called()
1210 log_stage
.assert_called_with("run_orders")
1211 log_orders
.assert_called_with([order_mock1
, order_mock2
,
1214 def test_all_orders(self
):
1215 trade_mock1
= mock
.Mock()
1216 trade_mock2
= mock
.Mock()
1218 order_mock1
= mock
.Mock()
1219 order_mock2
= mock
.Mock()
1220 order_mock3
= mock
.Mock()
1222 trade_mock1
.orders
= [order_mock1
, order_mock2
]
1223 trade_mock2
.orders
= [order_mock3
]
1225 order_mock1
.status
= "pending"
1226 order_mock2
.status
= "open"
1227 order_mock3
.status
= "open"
1229 portfolio
.TradeStore
.all
.append(trade_mock1
)
1230 portfolio
.TradeStore
.all
.append(trade_mock2
)
1232 orders
= portfolio
.TradeStore
.all_orders()
1233 self
.assertEqual(3, len(orders
))
1235 open_orders
= portfolio
.TradeStore
.all_orders(state
="open")
1236 self
.assertEqual(2, len(open_orders
))
1237 self
.assertEqual([order_mock2
, order_mock3
], open_orders
)
1239 @mock.patch.object(portfolio
.TradeStore
, "all_orders")
1240 def test_update_all_orders_status(self
, all_orders
):
1241 order_mock1
= mock
.Mock()
1242 order_mock2
= mock
.Mock()
1243 order_mock3
= mock
.Mock()
1244 all_orders
.return_value
= [order_mock1
, order_mock2
, order_mock3
]
1245 portfolio
.TradeStore
.update_all_orders_status()
1246 all_orders
.assert_called_with(state
="open")
1248 order_mock1
.get_status
.assert_called()
1249 order_mock2
.get_status
.assert_called()
1250 order_mock3
.get_status
.assert_called()
1253 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1254 class BalanceStoreTest(WebMockTestCase
):
1256 super(BalanceStoreTest
, self
).setUp()
1258 self
.fetch_balance
= {
1262 "exchange_total": 0,
1266 "exchange_free": D("6.0"),
1267 "exchange_used": D("1.2"),
1268 "exchange_total": D("7.2"),
1272 "exchange_free": 16,
1274 "exchange_total": 16,
1280 "exchange_total": 0,
1281 "margin_total": D("-1.0"),
1286 @mock.patch.object(helper
, "get_ticker")
1287 @mock.patch("portfolio.ReportStore.log_tickers")
1288 def test_in_currency(self
, log_tickers
, get_ticker
):
1289 portfolio
.BalanceStore
.all
= {
1290 "BTC": portfolio
.Balance("BTC", {
1292 "exchange_total":"0.65",
1293 "exchange_free": "0.35",
1294 "exchange_used": "0.30"}),
1295 "ETH": portfolio
.Balance("ETH", {
1297 "exchange_total": 3,
1299 "exchange_used": 0}),
1301 market
= mock
.Mock()
1302 get_ticker
.return_value
= {
1305 "average": D("0.1"),
1308 amounts
= portfolio
.BalanceStore
.in_currency("BTC", market
)
1309 self
.assertEqual("BTC", amounts
["ETH"].currency
)
1310 self
.assertEqual(D("0.65"), amounts
["BTC"].value
)
1311 self
.assertEqual(D("0.30"), amounts
["ETH"].value
)
1312 log_tickers
.assert_called_once_with(market
, amounts
, "BTC",
1314 log_tickers
.reset_mock()
1316 amounts
= portfolio
.BalanceStore
.in_currency("BTC", market
, compute_value
="bid")
1317 self
.assertEqual(D("0.65"), amounts
["BTC"].value
)
1318 self
.assertEqual(D("0.27"), amounts
["ETH"].value
)
1319 log_tickers
.assert_called_once_with(market
, amounts
, "BTC",
1321 log_tickers
.reset_mock()
1323 amounts
= portfolio
.BalanceStore
.in_currency("BTC", market
, compute_value
="bid", type="exchange_used")
1324 self
.assertEqual(D("0.30"), amounts
["BTC"].value
)
1325 self
.assertEqual(0, amounts
["ETH"].value
)
1326 log_tickers
.assert_called_once_with(market
, amounts
, "BTC",
1327 "bid", "exchange_used")
1328 log_tickers
.reset_mock()
1330 @mock.patch.object(portfolio
.ReportStore
, "log_balances")
1331 def test_fetch_balances(self
, log_balances
):
1332 market
= mock
.Mock()
1333 market
.fetch_all_balances
.return_value
= self
.fetch_balance
1335 portfolio
.BalanceStore
.fetch_balances(market
)
1336 self
.assertNotIn("ETC", portfolio
.BalanceStore
.currencies())
1337 self
.assertListEqual(["USDT", "XVG", "XMR"], list(portfolio
.BalanceStore
.currencies()))
1339 portfolio
.BalanceStore
.all
["ETC"] = portfolio
.Balance("ETC", {
1340 "exchange_total": "1", "exchange_free": "0",
1341 "exchange_used": "1" })
1342 portfolio
.BalanceStore
.fetch_balances(market
, tag
="foo")
1343 self
.assertEqual(0, portfolio
.BalanceStore
.all
["ETC"].total
)
1344 self
.assertListEqual(["USDT", "XVG", "XMR", "ETC"], list(portfolio
.BalanceStore
.currencies()))
1345 log_balances
.assert_called_with(market
, tag
="foo")
1347 @mock.patch.object(portfolio
.Portfolio
, "repartition")
1348 @mock.patch.object(portfolio
.ReportStore
, "log_balances")
1349 @mock.patch("store.ReportStore.log_dispatch")
1350 def test_dispatch_assets(self
, log_dispatch
, log_balances
, repartition
):
1351 market
= mock
.Mock()
1352 market
.fetch_all_balances
.return_value
= self
.fetch_balance
1353 portfolio
.BalanceStore
.fetch_balances(market
)
1355 self
.assertNotIn("XEM", portfolio
.BalanceStore
.currencies())
1357 repartition_hash
= {
1358 "XEM": (D("0.75"), "long"),
1359 "BTC": (D("0.26"), "long"),
1360 "DASH": (D("0.10"), "short"),
1362 repartition
.return_value
= repartition_hash
1364 amounts
= portfolio
.BalanceStore
.dispatch_assets(portfolio
.Amount("BTC", "11.1"))
1365 repartition
.assert_called_with(liquidity
="medium")
1366 self
.assertIn("XEM", portfolio
.BalanceStore
.currencies())
1367 self
.assertEqual(D("2.6"), amounts
["BTC"].value
)
1368 self
.assertEqual(D("7.5"), amounts
["XEM"].value
)
1369 self
.assertEqual(D("-1.0"), amounts
["DASH"].value
)
1370 log_balances
.assert_called_with(market
, tag
=None)
1371 log_dispatch
.assert_called_once_with(portfolio
.Amount("BTC",
1372 "11.1"), amounts
, "medium", repartition_hash
)
1374 def test_currencies(self
):
1375 portfolio
.BalanceStore
.all
= {
1376 "BTC": portfolio
.Balance("BTC", {
1378 "exchange_total":"0.65",
1379 "exchange_free": "0.35",
1380 "exchange_used": "0.30"}),
1381 "ETH": portfolio
.Balance("ETH", {
1383 "exchange_total": 3,
1385 "exchange_used": 0}),
1387 self
.assertListEqual(["BTC", "ETH"], list(portfolio
.BalanceStore
.currencies()))
1389 def test_as_json(self
):
1390 balance_mock1
= mock
.Mock()
1391 balance_mock1
.as_json
.return_value
= 1
1393 balance_mock2
= mock
.Mock()
1394 balance_mock2
.as_json
.return_value
= 2
1396 portfolio
.BalanceStore
.all
= {
1397 "BTC": balance_mock1
,
1398 "ETH": balance_mock2
,
1401 as_json
= portfolio
.BalanceStore
.as_json()
1402 self
.assertEqual(1, as_json
["BTC"])
1403 self
.assertEqual(2, as_json
["ETH"])
1406 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1407 class ComputationTest(WebMockTestCase
):
1408 def test_compute_value(self
):
1409 compute
= mock
.Mock()
1410 portfolio
.Computation
.compute_value("foo", "buy", compute_value
=compute
)
1411 compute
.assert_called_with("foo", "ask")
1413 compute
.reset_mock()
1414 portfolio
.Computation
.compute_value("foo", "sell", compute_value
=compute
)
1415 compute
.assert_called_with("foo", "bid")
1417 compute
.reset_mock()
1418 portfolio
.Computation
.compute_value("foo", "ask", compute_value
=compute
)
1419 compute
.assert_called_with("foo", "ask")
1421 compute
.reset_mock()
1422 portfolio
.Computation
.compute_value("foo", "bid", compute_value
=compute
)
1423 compute
.assert_called_with("foo", "bid")
1425 compute
.reset_mock()
1426 portfolio
.Computation
.computations
["test"] = compute
1427 portfolio
.Computation
.compute_value("foo", "bid", compute_value
="test")
1428 compute
.assert_called_with("foo", "bid")
1431 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1432 class TradeTest(WebMockTestCase
):
1434 def test_values_assertion(self
):
1435 value_from
= portfolio
.Amount("BTC", "1.0")
1436 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1437 value_to
= portfolio
.Amount("BTC", "1.0")
1438 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1439 self
.assertEqual("BTC", trade
.base_currency
)
1440 self
.assertEqual("ETH", trade
.currency
)
1442 with self
.assertRaises(AssertionError):
1443 portfolio
.Trade(value_from
, value_to
, "ETC")
1444 with self
.assertRaises(AssertionError):
1445 value_from
.linked_to
= None
1446 portfolio
.Trade(value_from
, value_to
, "ETH")
1447 with self
.assertRaises(AssertionError):
1448 value_from
.currency
= "ETH"
1449 portfolio
.Trade(value_from
, value_to
, "ETH")
1451 value_from
= portfolio
.Amount("BTC", 0)
1452 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1453 self
.assertEqual(0, trade
.value_from
.linked_to
)
1455 def test_action(self
):
1456 value_from
= portfolio
.Amount("BTC", "1.0")
1457 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1458 value_to
= portfolio
.Amount("BTC", "1.0")
1459 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1461 self
.assertIsNone(trade
.action
)
1463 value_from
= portfolio
.Amount("BTC", "1.0")
1464 value_from
.linked_to
= portfolio
.Amount("BTC", "1.0")
1465 value_to
= portfolio
.Amount("BTC", "2.0")
1466 trade
= portfolio
.Trade(value_from
, value_to
, "BTC")
1468 self
.assertIsNone(trade
.action
)
1470 value_from
= portfolio
.Amount("BTC", "0.5")
1471 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1472 value_to
= portfolio
.Amount("BTC", "1.0")
1473 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1475 self
.assertEqual("acquire", trade
.action
)
1477 value_from
= portfolio
.Amount("BTC", "0")
1478 value_from
.linked_to
= portfolio
.Amount("ETH", "0")
1479 value_to
= portfolio
.Amount("BTC", "-1.0")
1480 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1482 self
.assertEqual("acquire", trade
.action
)
1484 def test_order_action(self
):
1485 value_from
= portfolio
.Amount("BTC", "0.5")
1486 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1487 value_to
= portfolio
.Amount("BTC", "1.0")
1488 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1490 self
.assertEqual("buy", trade
.order_action(False))
1491 self
.assertEqual("sell", trade
.order_action(True))
1493 value_from
= portfolio
.Amount("BTC", "0")
1494 value_from
.linked_to
= portfolio
.Amount("ETH", "0")
1495 value_to
= portfolio
.Amount("BTC", "-1.0")
1496 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1498 self
.assertEqual("sell", trade
.order_action(False))
1499 self
.assertEqual("buy", trade
.order_action(True))
1501 def test_trade_type(self
):
1502 value_from
= portfolio
.Amount("BTC", "0.5")
1503 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1504 value_to
= portfolio
.Amount("BTC", "1.0")
1505 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1507 self
.assertEqual("long", trade
.trade_type
)
1509 value_from
= portfolio
.Amount("BTC", "0")
1510 value_from
.linked_to
= portfolio
.Amount("ETH", "0")
1511 value_to
= portfolio
.Amount("BTC", "-1.0")
1512 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1514 self
.assertEqual("short", trade
.trade_type
)
1516 def test_filled_amount(self
):
1517 value_from
= portfolio
.Amount("BTC", "0.5")
1518 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1519 value_to
= portfolio
.Amount("BTC", "1.0")
1520 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1522 order1
= mock
.Mock()
1523 order1
.filled_amount
.return_value
= portfolio
.Amount("ETH", "0.3")
1525 order2
= mock
.Mock()
1526 order2
.filled_amount
.return_value
= portfolio
.Amount("ETH", "0.01")
1527 trade
.orders
.append(order1
)
1528 trade
.orders
.append(order2
)
1530 self
.assertEqual(portfolio
.Amount("ETH", "0.31"), trade
.filled_amount())
1531 order1
.filled_amount
.assert_called_with(in_base_currency
=False)
1532 order2
.filled_amount
.assert_called_with(in_base_currency
=False)
1534 self
.assertEqual(portfolio
.Amount("ETH", "0.31"), trade
.filled_amount(in_base_currency
=False))
1535 order1
.filled_amount
.assert_called_with(in_base_currency
=False)
1536 order2
.filled_amount
.assert_called_with(in_base_currency
=False)
1538 self
.assertEqual(portfolio
.Amount("ETH", "0.31"), trade
.filled_amount(in_base_currency
=True))
1539 order1
.filled_amount
.assert_called_with(in_base_currency
=True)
1540 order2
.filled_amount
.assert_called_with(in_base_currency
=True)
1542 @mock.patch.object(helper
, "get_ticker")
1543 @mock.patch.object(portfolio
.Computation
, "compute_value")
1544 @mock.patch.object(portfolio
.Trade
, "filled_amount")
1545 @mock.patch.object(portfolio
, "Order")
1546 def test_prepare_order(self
, Order
, filled_amount
, compute_value
, get_ticker
):
1547 Order
.return_value
= "Order"
1549 with self
.subTest(desc
="Nothing to do"):
1550 value_from
= portfolio
.Amount("BTC", "10")
1551 value_from
.rate
= D("0.1")
1552 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1553 value_to
= portfolio
.Amount("BTC", "10")
1554 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1556 trade
.prepare_order()
1558 filled_amount
.assert_not_called()
1559 compute_value
.assert_not_called()
1560 self
.assertEqual(0, len(trade
.orders
))
1561 Order
.assert_not_called()
1563 get_ticker
.return_value
= { "inverted": False }
1564 with self
.subTest(desc
="Already filled"),\
1565 mock
.patch("portfolio.ReportStore") as report_store
:
1566 filled_amount
.return_value
= portfolio
.Amount("FOO", "100")
1567 compute_value
.return_value
= D("0.125")
1569 value_from
= portfolio
.Amount("BTC", "10")
1570 value_from
.rate
= D("0.1")
1571 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1572 value_to
= portfolio
.Amount("BTC", "0")
1573 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1575 trade
.prepare_order()
1577 filled_amount
.assert_called_with(in_base_currency
=False)
1578 compute_value
.assert_called_with(get_ticker
.return_value
, "sell", compute_value
="default")
1579 self
.assertEqual(0, len(trade
.orders
))
1580 report_store
.log_error
.assert_called_with("prepare_order", message
=mock
.ANY
)
1581 Order
.assert_not_called()
1583 with self
.subTest(action
="dispose", inverted
=False):
1584 filled_amount
.return_value
= portfolio
.Amount("FOO", "60")
1585 compute_value
.return_value
= D("0.125")
1587 value_from
= portfolio
.Amount("BTC", "10")
1588 value_from
.rate
= D("0.1")
1589 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1590 value_to
= portfolio
.Amount("BTC", "1")
1591 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1593 trade
.prepare_order()
1595 filled_amount
.assert_called_with(in_base_currency
=False)
1596 compute_value
.assert_called_with(get_ticker
.return_value
, "sell", compute_value
="default")
1597 self
.assertEqual(1, len(trade
.orders
))
1598 Order
.assert_called_with("sell", portfolio
.Amount("FOO", 30),
1599 D("0.125"), "BTC", "long", "market",
1600 trade
, close_if_possible
=False)
1602 with self
.subTest(action
="acquire", inverted
=False):
1603 filled_amount
.return_value
= portfolio
.Amount("BTC", "3")
1604 compute_value
.return_value
= D("0.125")
1606 value_from
= portfolio
.Amount("BTC", "1")
1607 value_from
.rate
= D("0.1")
1608 value_from
.linked_to
= portfolio
.Amount("FOO", "10")
1609 value_to
= portfolio
.Amount("BTC", "10")
1610 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1612 trade
.prepare_order()
1614 filled_amount
.assert_called_with(in_base_currency
=True)
1615 compute_value
.assert_called_with(get_ticker
.return_value
, "buy", compute_value
="default")
1616 self
.assertEqual(1, len(trade
.orders
))
1618 Order
.assert_called_with("buy", portfolio
.Amount("FOO", 48),
1619 D("0.125"), "BTC", "long", "market",
1620 trade
, close_if_possible
=False)
1622 with self
.subTest(close_if_possible
=True):
1623 filled_amount
.return_value
= portfolio
.Amount("FOO", "0")
1624 compute_value
.return_value
= D("0.125")
1626 value_from
= portfolio
.Amount("BTC", "10")
1627 value_from
.rate
= D("0.1")
1628 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1629 value_to
= portfolio
.Amount("BTC", "0")
1630 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1632 trade
.prepare_order()
1634 filled_amount
.assert_called_with(in_base_currency
=False)
1635 compute_value
.assert_called_with(get_ticker
.return_value
, "sell", compute_value
="default")
1636 self
.assertEqual(1, len(trade
.orders
))
1637 Order
.assert_called_with("sell", portfolio
.Amount("FOO", 100),
1638 D("0.125"), "BTC", "long", "market",
1639 trade
, close_if_possible
=True)
1641 get_ticker
.return_value
= { "inverted": True, "original": {}
}
1642 with self
.subTest(action
="dispose", inverted
=True):
1643 filled_amount
.return_value
= portfolio
.Amount("FOO", "300")
1644 compute_value
.return_value
= D("125")
1646 value_from
= portfolio
.Amount("BTC", "10")
1647 value_from
.rate
= D("0.01")
1648 value_from
.linked_to
= portfolio
.Amount("FOO", "1000")
1649 value_to
= portfolio
.Amount("BTC", "1")
1650 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1652 trade
.prepare_order(compute_value
="foo")
1654 filled_amount
.assert_called_with(in_base_currency
=True)
1655 compute_value
.assert_called_with(get_ticker
.return_value
["original"], "buy", compute_value
="foo")
1656 self
.assertEqual(1, len(trade
.orders
))
1657 Order
.assert_called_with("buy", portfolio
.Amount("BTC", D("4.8")),
1658 D("125"), "FOO", "long", "market",
1659 trade
, close_if_possible
=False)
1661 with self
.subTest(action
="acquire", inverted
=True):
1662 filled_amount
.return_value
= portfolio
.Amount("BTC", "4")
1663 compute_value
.return_value
= D("125")
1665 value_from
= portfolio
.Amount("BTC", "1")
1666 value_from
.rate
= D("0.01")
1667 value_from
.linked_to
= portfolio
.Amount("FOO", "100")
1668 value_to
= portfolio
.Amount("BTC", "10")
1669 trade
= portfolio
.Trade(value_from
, value_to
, "FOO", market
="market")
1671 trade
.prepare_order(compute_value
="foo")
1673 filled_amount
.assert_called_with(in_base_currency
=False)
1674 compute_value
.assert_called_with(get_ticker
.return_value
["original"], "sell", compute_value
="foo")
1675 self
.assertEqual(1, len(trade
.orders
))
1676 Order
.assert_called_with("sell", portfolio
.Amount("BTC", D("5")),
1677 D("125"), "FOO", "long", "market",
1678 trade
, close_if_possible
=False)
1681 @mock.patch.object(portfolio
.Trade
, "prepare_order")
1682 def test_update_order(self
, prepare_order
):
1683 order_mock
= mock
.Mock()
1684 new_order_mock
= mock
.Mock()
1686 value_from
= portfolio
.Amount("BTC", "0.5")
1687 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1688 value_to
= portfolio
.Amount("BTC", "1.0")
1689 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1690 prepare_order
.return_value
= new_order_mock
1692 for i
in [0, 1, 3, 4, 6]:
1693 with self
.subTest(tick
=i
),\
1694 mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1695 trade
.update_order(order_mock
, i
)
1696 order_mock
.cancel
.assert_not_called()
1697 new_order_mock
.run
.assert_not_called()
1698 log_order
.assert_called_once_with(order_mock
, i
,
1699 update
="waiting", compute_value
=None, new_order
=None)
1701 order_mock
.reset_mock()
1702 new_order_mock
.reset_mock()
1705 with mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1706 trade
.update_order(order_mock
, 2)
1707 order_mock
.cancel
.assert_called()
1708 new_order_mock
.run
.assert_called()
1709 prepare_order
.assert_called()
1710 log_order
.assert_called()
1711 self
.assertEqual(2, log_order
.call_count
)
1713 mock
.call(order_mock
, 2, update
="adjusting",
1714 compute_value
='lambda x, y: (x[y] + x["average"]) / 2',
1715 new_order
=new_order_mock
),
1716 mock
.call(order_mock
, 2, new_order
=new_order_mock
),
1718 log_order
.assert_has_calls(calls
)
1720 order_mock
.reset_mock()
1721 new_order_mock
.reset_mock()
1724 with mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1725 trade
.update_order(order_mock
, 5)
1726 order_mock
.cancel
.assert_called()
1727 new_order_mock
.run
.assert_called()
1728 prepare_order
.assert_called()
1729 self
.assertEqual(2, log_order
.call_count
)
1730 log_order
.assert_called()
1732 mock
.call(order_mock
, 5, update
="adjusting",
1733 compute_value
='lambda x, y: (x[y]*2 + x["average"]) / 3',
1734 new_order
=new_order_mock
),
1735 mock
.call(order_mock
, 5, new_order
=new_order_mock
),
1737 log_order
.assert_has_calls(calls
)
1739 order_mock
.reset_mock()
1740 new_order_mock
.reset_mock()
1743 with mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1744 trade
.update_order(order_mock
, 7)
1745 order_mock
.cancel
.assert_called()
1746 new_order_mock
.run
.assert_called()
1747 prepare_order
.assert_called_with(compute_value
="default")
1748 log_order
.assert_called()
1749 self
.assertEqual(2, log_order
.call_count
)
1751 mock
.call(order_mock
, 7, update
="market_fallback",
1752 compute_value
='default',
1753 new_order
=new_order_mock
),
1754 mock
.call(order_mock
, 7, new_order
=new_order_mock
),
1756 log_order
.assert_has_calls(calls
)
1758 order_mock
.reset_mock()
1759 new_order_mock
.reset_mock()
1762 for i
in [10, 13, 16]:
1763 with self
.subTest(tick
=i
), mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1764 trade
.update_order(order_mock
, i
)
1765 order_mock
.cancel
.assert_called()
1766 new_order_mock
.run
.assert_called()
1767 prepare_order
.assert_called_with(compute_value
="default")
1768 log_order
.assert_called()
1769 self
.assertEqual(2, log_order
.call_count
)
1771 mock
.call(order_mock
, i
, update
="market_adjust",
1772 compute_value
='default',
1773 new_order
=new_order_mock
),
1774 mock
.call(order_mock
, i
, new_order
=new_order_mock
),
1776 log_order
.assert_has_calls(calls
)
1778 order_mock
.reset_mock()
1779 new_order_mock
.reset_mock()
1782 for i
in [8, 9, 11, 12]:
1783 with self
.subTest(tick
=i
), mock
.patch
.object(portfolio
.ReportStore
, "log_order") as log_order
:
1784 trade
.update_order(order_mock
, i
)
1785 order_mock
.cancel
.assert_not_called()
1786 new_order_mock
.run
.assert_not_called()
1787 log_order
.assert_called_once_with(order_mock
, i
, update
="waiting",
1788 compute_value
=None, new_order
=None)
1790 order_mock
.reset_mock()
1791 new_order_mock
.reset_mock()
1795 @mock.patch.object(portfolio
.ReportStore
, "print_log")
1796 def test_print_with_order(self
, print_log
):
1797 value_from
= portfolio
.Amount("BTC", "0.5")
1798 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1799 value_to
= portfolio
.Amount("BTC", "1.0")
1800 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1802 order_mock1
= mock
.Mock()
1803 order_mock1
.__repr__
= mock
.Mock()
1804 order_mock1
.__repr__
.return_value
= "Mock 1"
1805 order_mock2
= mock
.Mock()
1806 order_mock2
.__repr__
= mock
.Mock()
1807 order_mock2
.__repr__
.return_value
= "Mock 2"
1808 order_mock1
.mouvements
= []
1809 mouvement_mock1
= mock
.Mock()
1810 mouvement_mock1
.__repr__
= mock
.Mock()
1811 mouvement_mock1
.__repr__
.return_value
= "Mouvement 1"
1812 mouvement_mock2
= mock
.Mock()
1813 mouvement_mock2
.__repr__
= mock
.Mock()
1814 mouvement_mock2
.__repr__
.return_value
= "Mouvement 2"
1815 order_mock2
.mouvements
= [
1816 mouvement_mock1
, mouvement_mock2
1818 trade
.orders
.append(order_mock1
)
1819 trade
.orders
.append(order_mock2
)
1821 trade
.print_with_order()
1823 print_log
.assert_called()
1824 calls
= print_log
.mock_calls
1825 self
.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(calls
[0][1][0]))
1826 self
.assertEqual("\tMock 1", str(calls
[1][1][0]))
1827 self
.assertEqual("\tMock 2", str(calls
[2][1][0]))
1828 self
.assertEqual("\t\tMouvement 1", str(calls
[3][1][0]))
1829 self
.assertEqual("\t\tMouvement 2", str(calls
[4][1][0]))
1831 def test__repr(self
):
1832 value_from
= portfolio
.Amount("BTC", "0.5")
1833 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1834 value_to
= portfolio
.Amount("BTC", "1.0")
1835 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1837 self
.assertEqual("Trade(0.50000000 BTC [10.00000000 ETH] -> 1.00000000 BTC in ETH, acquire)", str(trade
))
1839 def test_as_json(self
):
1840 value_from
= portfolio
.Amount("BTC", "0.5")
1841 value_from
.linked_to
= portfolio
.Amount("ETH", "10.0")
1842 value_to
= portfolio
.Amount("BTC", "1.0")
1843 trade
= portfolio
.Trade(value_from
, value_to
, "ETH")
1845 as_json
= trade
.as_json()
1846 self
.assertEqual("acquire", as_json
["action"])
1847 self
.assertEqual(D("0.5"), as_json
["from"])
1848 self
.assertEqual(D("1.0"), as_json
["to"])
1849 self
.assertEqual("ETH", as_json
["currency"])
1850 self
.assertEqual("BTC", as_json
["base_currency"])
1852 @unittest.skipUnless("unit" in limits
, "Unit skipped")
1853 class OrderTest(WebMockTestCase
):
1854 def test_values(self
):
1855 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1856 D("0.1"), "BTC", "long", "market", "trade")
1857 self
.assertEqual("buy", order
.action
)
1858 self
.assertEqual(10, order
.amount
.value
)
1859 self
.assertEqual("ETH", order
.amount
.currency
)
1860 self
.assertEqual(D("0.1"), order
.rate
)
1861 self
.assertEqual("BTC", order
.base_currency
)
1862 self
.assertEqual("market", order
.market
)
1863 self
.assertEqual("long", order
.trade_type
)
1864 self
.assertEqual("pending", order
.status
)
1865 self
.assertEqual("trade", order
.trade
)
1866 self
.assertIsNone(order
.id)
1867 self
.assertFalse(order
.close_if_possible
)
1869 def test__repr(self
):
1870 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1871 D("0.1"), "BTC", "long", "market", "trade")
1872 self
.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending])", repr(order
))
1874 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1875 D("0.1"), "BTC", "long", "market", "trade",
1876 close_if_possible
=True)
1877 self
.assertEqual("Order(buy long 10.00000000 ETH at 0.1 BTC [pending] ✂)", repr(order
))
1879 def test_as_json(self
):
1880 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1881 D("0.1"), "BTC", "long", "market", "trade")
1882 mouvement_mock1
= mock
.Mock()
1883 mouvement_mock1
.as_json
.return_value
= 1
1884 mouvement_mock2
= mock
.Mock()
1885 mouvement_mock2
.as_json
.return_value
= 2
1887 order
.mouvements
= [mouvement_mock1
, mouvement_mock2
]
1888 as_json
= order
.as_json()
1889 self
.assertEqual("buy", as_json
["action"])
1890 self
.assertEqual("long", as_json
["trade_type"])
1891 self
.assertEqual(10, as_json
["amount"])
1892 self
.assertEqual("ETH", as_json
["currency"])
1893 self
.assertEqual("BTC", as_json
["base_currency"])
1894 self
.assertEqual(D("0.1"), as_json
["rate"])
1895 self
.assertEqual("pending", as_json
["status"])
1896 self
.assertEqual(False, as_json
["close_if_possible"])
1897 self
.assertIsNone(as_json
["id"])
1898 self
.assertEqual([1, 2], as_json
["mouvements"])
1900 def test_account(self
):
1901 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1902 D("0.1"), "BTC", "long", "market", "trade")
1903 self
.assertEqual("exchange", order
.account
)
1905 order
= portfolio
.Order("sell", portfolio
.Amount("ETH", 10),
1906 D("0.1"), "BTC", "short", "market", "trade")
1907 self
.assertEqual("margin", order
.account
)
1909 def test_pending(self
):
1910 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1911 D("0.1"), "BTC", "long", "market", "trade")
1912 self
.assertTrue(order
.pending
)
1913 order
.status
= "open"
1914 self
.assertFalse(order
.pending
)
1916 def test_open(self
):
1917 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1918 D("0.1"), "BTC", "long", "market", "trade")
1919 self
.assertFalse(order
.open)
1920 order
.status
= "open"
1921 self
.assertTrue(order
.open)
1923 def test_finished(self
):
1924 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1925 D("0.1"), "BTC", "long", "market", "trade")
1926 self
.assertFalse(order
.finished
)
1927 order
.status
= "closed"
1928 self
.assertTrue(order
.finished
)
1929 order
.status
= "canceled"
1930 self
.assertTrue(order
.finished
)
1931 order
.status
= "error"
1932 self
.assertTrue(order
.finished
)
1934 @mock.patch.object(portfolio
.Order
, "fetch")
1935 @mock.patch("portfolio.ReportStore")
1936 def test_cancel(self
, report_store
, fetch
):
1937 market
= mock
.Mock()
1938 portfolio
.TradeStore
.debug
= True
1939 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1940 D("0.1"), "BTC", "long", market
, "trade")
1941 order
.status
= "open"
1944 market
.cancel_order
.assert_not_called()
1945 report_store
.log_debug_action
.assert_called_once()
1946 report_store
.log_debug_action
.reset_mock()
1947 self
.assertEqual("canceled", order
.status
)
1949 portfolio
.TradeStore
.debug
= False
1950 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1951 D("0.1"), "BTC", "long", market
, "trade")
1952 order
.status
= "open"
1956 market
.cancel_order
.assert_called_with(42)
1957 fetch
.assert_called_once()
1958 report_store
.log_debug_action
.assert_not_called()
1960 def test_dust_amount_remaining(self
):
1961 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1962 D("0.1"), "BTC", "long", "market", "trade")
1963 order
.remaining_amount
= mock
.Mock(return_value
=portfolio
.Amount("ETH", 1))
1964 self
.assertFalse(order
.dust_amount_remaining())
1966 order
.remaining_amount
= mock
.Mock(return_value
=portfolio
.Amount("ETH", D("0.0001")))
1967 self
.assertTrue(order
.dust_amount_remaining())
1969 @mock.patch.object(portfolio
.Order
, "fetch")
1970 @mock.patch.object(portfolio
.Order
, "filled_amount", return_value
=portfolio
.Amount("ETH", 1))
1971 def test_remaining_amount(self
, filled_amount
, fetch
):
1972 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1973 D("0.1"), "BTC", "long", "market", "trade")
1975 self
.assertEqual(9, order
.remaining_amount().value
)
1976 order
.fetch
.assert_not_called()
1978 order
.status
= "open"
1979 self
.assertEqual(9, order
.remaining_amount().value
)
1980 fetch
.assert_called_once()
1982 @mock.patch.object(portfolio
.Order
, "fetch")
1983 def test_filled_amount(self
, fetch
):
1984 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
1985 D("0.1"), "BTC", "long", "market", "trade")
1986 order
.mouvements
.append(portfolio
.Mouvement("ETH", "BTC", {
1987 "tradeID": 42, "type": "buy", "fee": "0.0015",
1988 "date": "2017-12-30 12:00:12", "rate": "0.1",
1989 "amount": "3", "total": "0.3"
1991 order
.mouvements
.append(portfolio
.Mouvement("ETH", "BTC", {
1992 "tradeID": 43, "type": "buy", "fee": "0.0015",
1993 "date": "2017-12-30 13:00:12", "rate": "0.2",
1994 "amount": "2", "total": "0.4"
1996 self
.assertEqual(portfolio
.Amount("ETH", 5), order
.filled_amount())
1997 fetch
.assert_not_called()
1998 order
.status
= "open"
1999 self
.assertEqual(portfolio
.Amount("ETH", 5), order
.filled_amount(in_base_currency
=False))
2000 fetch
.assert_called_once()
2001 self
.assertEqual(portfolio
.Amount("BTC", "0.7"), order
.filled_amount(in_base_currency
=True))
2003 def test_fetch_mouvements(self
):
2004 market
= mock
.Mock()
2005 market
.privatePostReturnOrderTrades
.return_value
= [
2007 "tradeID": 42, "type": "buy", "fee": "0.0015",
2008 "date": "2017-12-30 12:00:12", "rate": "0.1",
2009 "amount": "3", "total": "0.3"
2012 "tradeID": 43, "type": "buy", "fee": "0.0015",
2013 "date": "2017-12-30 13:00:12", "rate": "0.2",
2014 "amount": "2", "total": "0.4"
2017 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2018 D("0.1"), "BTC", "long", market
, "trade")
2020 order
.mouvements
= ["Foo", "Bar", "Baz"]
2022 order
.fetch_mouvements()
2024 market
.privatePostReturnOrderTrades
.assert_called_with({"orderNumber": 12}
)
2025 self
.assertEqual(2, len(order
.mouvements
))
2026 self
.assertEqual(42, order
.mouvements
[0].id)
2027 self
.assertEqual(43, order
.mouvements
[1].id)
2029 market
.privatePostReturnOrderTrades
.side_effect
= portfolio
.ExchangeError
2030 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2031 D("0.1"), "BTC", "long", market
, "trade")
2032 order
.fetch_mouvements()
2033 self
.assertEqual(0, len(order
.mouvements
))
2035 @mock.patch("portfolio.ReportStore")
2036 def test_mark_finished_order(self
, report_store
):
2037 market
= mock
.Mock()
2038 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2039 D("0.1"), "BTC", "short", market
, "trade",
2040 close_if_possible
=True)
2041 order
.status
= "closed"
2042 portfolio
.TradeStore
.debug
= False
2044 order
.mark_finished_order()
2045 market
.close_margin_position
.assert_called_with("ETH", "BTC")
2046 market
.close_margin_position
.reset_mock()
2048 order
.status
= "open"
2049 order
.mark_finished_order()
2050 market
.close_margin_position
.assert_not_called()
2052 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2053 D("0.1"), "BTC", "short", market
, "trade",
2054 close_if_possible
=False)
2055 order
.status
= "closed"
2056 order
.mark_finished_order()
2057 market
.close_margin_position
.assert_not_called()
2059 order
= portfolio
.Order("sell", portfolio
.Amount("ETH", 10),
2060 D("0.1"), "BTC", "short", market
, "trade",
2061 close_if_possible
=True)
2062 order
.status
= "closed"
2063 order
.mark_finished_order()
2064 market
.close_margin_position
.assert_not_called()
2066 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2067 D("0.1"), "BTC", "long", market
, "trade",
2068 close_if_possible
=True)
2069 order
.status
= "closed"
2070 order
.mark_finished_order()
2071 market
.close_margin_position
.assert_not_called()
2073 portfolio
.TradeStore
.debug
= True
2075 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2076 D("0.1"), "BTC", "short", market
, "trade",
2077 close_if_possible
=True)
2078 order
.status
= "closed"
2080 order
.mark_finished_order()
2081 market
.close_margin_position
.assert_not_called()
2082 report_store
.log_debug_action
.assert_called_once()
2084 @mock.patch.object(portfolio
.Order
, "fetch_mouvements")
2085 @mock.patch("portfolio.ReportStore")
2086 def test_fetch(self
, report_store
, fetch_mouvements
):
2087 time
= self
.time
.time()
2088 with mock
.patch
.object(portfolio
.time
, "time") as time_mock
:
2089 market
= mock
.Mock()
2090 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2091 D("0.1"), "BTC", "long", market
, "trade")
2093 with self
.subTest(debug
=True):
2094 portfolio
.TradeStore
.debug
= True
2096 time_mock
.assert_not_called()
2097 report_store
.log_debug_action
.assert_called_once()
2098 report_store
.log_debug_action
.reset_mock()
2099 order
.fetch(force
=True)
2100 time_mock
.assert_not_called()
2101 market
.fetch_order
.assert_not_called()
2102 fetch_mouvements
.assert_not_called()
2103 report_store
.log_debug_action
.assert_called_once()
2104 report_store
.log_debug_action
.reset_mock()
2105 self
.assertIsNone(order
.fetch_cache_timestamp
)
2107 with self
.subTest(debug
=False):
2108 portfolio
.TradeStore
.debug
= False
2109 time_mock
.return_value
= time
2110 market
.fetch_order
.return_value
= {
2112 "datetime": "timestamp"
2116 market
.fetch_order
.assert_called_once()
2117 fetch_mouvements
.assert_called_once()
2118 self
.assertEqual("foo", order
.status
)
2119 self
.assertEqual("timestamp", order
.timestamp
)
2120 self
.assertEqual(time
, order
.fetch_cache_timestamp
)
2121 self
.assertEqual(1, len(order
.results
))
2123 market
.fetch_order
.reset_mock()
2124 fetch_mouvements
.reset_mock()
2126 time_mock
.return_value
= time
+ 8
2128 market
.fetch_order
.assert_not_called()
2129 fetch_mouvements
.assert_not_called()
2131 order
.fetch(force
=True)
2132 market
.fetch_order
.assert_called_once()
2133 fetch_mouvements
.assert_called_once()
2135 market
.fetch_order
.reset_mock()
2136 fetch_mouvements
.reset_mock()
2138 time_mock
.return_value
= time
+ 19
2140 market
.fetch_order
.assert_called_once()
2141 fetch_mouvements
.assert_called_once()
2142 report_store
.log_debug_action
.assert_not_called()
2144 @mock.patch.object(portfolio
.Order
, "fetch")
2145 @mock.patch.object(portfolio
.Order
, "mark_finished_order")
2146 @mock.patch("portfolio.ReportStore")
2147 def test_get_status(self
, report_store
, mark_finished_order
, fetch
):
2148 with self
.subTest(debug
=True):
2149 portfolio
.TradeStore
.debug
= True
2150 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2151 D("0.1"), "BTC", "long", "market", "trade")
2152 self
.assertEqual("pending", order
.get_status())
2153 fetch
.assert_not_called()
2154 report_store
.log_debug_action
.assert_called_once()
2156 with self
.subTest(debug
=False, finished
=False):
2157 portfolio
.TradeStore
.debug
= False
2158 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2159 D("0.1"), "BTC", "long", "market", "trade")
2161 def update_status():
2162 order
.status
= "open"
2163 return update_status
2164 fetch
.side_effect
= _fetch(order
)
2165 self
.assertEqual("open", order
.get_status())
2166 mark_finished_order
.assert_not_called()
2167 fetch
.assert_called_once()
2169 mark_finished_order
.reset_mock()
2171 with self
.subTest(debug
=False, finished
=True):
2172 portfolio
.TradeStore
.debug
= False
2173 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2174 D("0.1"), "BTC", "long", "market", "trade")
2176 def update_status():
2177 order
.status
= "closed"
2178 return update_status
2179 fetch
.side_effect
= _fetch(order
)
2180 self
.assertEqual("closed", order
.get_status())
2181 mark_finished_order
.assert_called_once()
2182 fetch
.assert_called_once()
2185 market
= mock
.Mock()
2187 market
.order_precision
.return_value
= 4
2188 with self
.subTest(debug
=True),\
2189 mock
.patch('portfolio.ReportStore') as report_store
:
2190 portfolio
.TradeStore
.debug
= True
2191 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2192 D("0.1"), "BTC", "long", market
, "trade")
2194 market
.create_order
.assert_not_called()
2195 report_store
.log_debug_action
.assert_called_with("market.create_order('ETH/BTC', 'limit', 'buy', 10.0000, price=0.1, account=exchange)")
2196 self
.assertEqual("open", order
.status
)
2197 self
.assertEqual(1, len(order
.results
))
2198 self
.assertEqual(-1, order
.id)
2200 market
.create_order
.reset_mock()
2201 with self
.subTest(debug
=False):
2202 portfolio
.TradeStore
.debug
= False
2203 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2204 D("0.1"), "BTC", "long", market
, "trade")
2205 market
.create_order
.return_value
= { "id": 123 }
2207 market
.create_order
.assert_called_once()
2208 self
.assertEqual(1, len(order
.results
))
2209 self
.assertEqual("open", order
.status
)
2211 market
.create_order
.reset_mock()
2212 with self
.subTest(exception
=True),\
2213 mock
.patch('portfolio.ReportStore') as report_store
:
2214 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 10),
2215 D("0.1"), "BTC", "long", market
, "trade")
2216 market
.create_order
.side_effect
= Exception("bouh")
2218 market
.create_order
.assert_called_once()
2219 self
.assertEqual(0, len(order
.results
))
2220 self
.assertEqual("error", order
.status
)
2221 report_store
.log_error
.assert_called_once()
2223 market
.create_order
.reset_mock()
2224 with self
.subTest(dust_amount_exception
=True),\
2225 mock
.patch
.object(portfolio
.Order
, "mark_finished_order") as mark_finished_order
:
2226 order
= portfolio
.Order("buy", portfolio
.Amount("ETH", 0.001),
2227 D("0.1"), "BTC", "long", market
, "trade")
2228 market
.create_order
.side_effect
= portfolio
.ExchangeNotAvailable
2230 market
.create_order
.assert_called_once()
2231 self
.assertEqual(0, len(order
.results
))
2232 self
.assertEqual("closed", order
.status
)
2233 mark_finished_order
.assert_called_once()
2236 @unittest.skipUnless("unit" in limits
, "Unit skipped")
2237 class MouvementTest(WebMockTestCase
):
2238 def test_values(self
):
2239 mouvement
= portfolio
.Mouvement("ETH", "BTC", {
2240 "tradeID": 42, "type": "buy", "fee": "0.0015",
2241 "date": "2017-12-30 12:00:12", "rate": "0.1",
2242 "amount": "10", "total": "1"
2244 self
.assertEqual("ETH", mouvement
.currency
)
2245 self
.assertEqual("BTC", mouvement
.base_currency
)
2246 self
.assertEqual(42, mouvement
.id)
2247 self
.assertEqual("buy", mouvement
.action
)
2248 self
.assertEqual(D("0.0015"), mouvement
.fee_rate
)
2249 self
.assertEqual(portfolio
.datetime(2017, 12, 30, 12, 0, 12), mouvement
.date
)
2250 self
.assertEqual(D("0.1"), mouvement
.rate
)
2251 self
.assertEqual(portfolio
.Amount("ETH", "10"), mouvement
.total
)
2252 self
.assertEqual(portfolio
.Amount("BTC", "1"), mouvement
.total_in_base
)
2254 mouvement
= portfolio
.Mouvement("ETH", "BTC", { "foo": "bar" }
)
2255 self
.assertIsNone(mouvement
.date
)
2256 self
.assertIsNone(mouvement
.id)
2257 self
.assertIsNone(mouvement
.action
)
2258 self
.assertEqual(-1, mouvement
.fee_rate
)
2259 self
.assertEqual(0, mouvement
.rate
)
2260 self
.assertEqual(portfolio
.Amount("ETH", 0), mouvement
.total
)
2261 self
.assertEqual(portfolio
.Amount("BTC", 0), mouvement
.total_in_base
)
2263 def test__repr(self
):
2264 mouvement
= portfolio
.Mouvement("ETH", "BTC", {
2265 "tradeID": 42, "type": "buy", "fee": "0.0015",
2266 "date": "2017-12-30 12:00:12", "rate": "0.1",
2267 "amount": "10", "total": "1"
2269 self
.assertEqual("Mouvement(2017-12-30 12:00:12 ; buy 10.00000000 ETH (1.00000000 BTC) fee: 0.1500%)", repr(mouvement
))
2271 mouvement
= portfolio
.Mouvement("ETH", "BTC", {
2272 "tradeID": 42, "type": "buy",
2273 "date": "garbage", "rate": "0.1",
2274 "amount": "10", "total": "1"
2276 self
.assertEqual("Mouvement(No date ; buy 10.00000000 ETH (1.00000000 BTC))", repr(mouvement
))
2278 def test_as_json(self
):
2279 mouvement
= portfolio
.Mouvement("ETH", "BTC", {
2280 "tradeID": 42, "type": "buy", "fee": "0.0015",
2281 "date": "2017-12-30 12:00:12", "rate": "0.1",
2282 "amount": "10", "total": "1"
2284 as_json
= mouvement
.as_json()
2286 self
.assertEqual(D("0.0015"), as_json
["fee_rate"])
2287 self
.assertEqual(portfolio
.datetime(2017, 12, 30, 12, 0, 12), as_json
["date"])
2288 self
.assertEqual("buy", as_json
["action"])
2289 self
.assertEqual(D("10"), as_json
["total"])
2290 self
.assertEqual(D("1"), as_json
["total_in_base"])
2291 self
.assertEqual("BTC", as_json
["base_currency"])
2292 self
.assertEqual("ETH", as_json
["currency"])
2294 @unittest.skipUnless("unit" in limits
, "Unit skipped")
2295 class ReportStoreTest(WebMockTestCase
):
2296 def test_add_log(self
):
2297 portfolio
.ReportStore
.add_log({"foo": "bar"}
)
2299 self
.assertEqual({"foo": "bar", "date": mock.ANY}
, portfolio
.ReportStore
.logs
[0])
2301 def test_set_verbose(self
):
2302 with self
.subTest(verbose
=True):
2303 portfolio
.ReportStore
.set_verbose(True)
2304 self
.assertTrue(portfolio
.ReportStore
.verbose_print
)
2306 with self
.subTest(verbose
=False):
2307 portfolio
.ReportStore
.set_verbose(False)
2308 self
.assertFalse(portfolio
.ReportStore
.verbose_print
)
2310 def test_print_log(self
):
2311 with self
.subTest(verbose
=True),\
2312 mock
.patch('sys.stdout', new_callable
=StringIO
) as stdout_mock
:
2313 portfolio
.ReportStore
.set_verbose(True)
2314 portfolio
.ReportStore
.print_log("Coucou")
2315 portfolio
.ReportStore
.print_log(portfolio
.Amount("BTC", 1))
2316 self
.assertEqual(stdout_mock
.getvalue(), "Coucou\n1.00000000 BTC\n")
2318 with self
.subTest(verbose
=False),\
2319 mock
.patch('sys.stdout', new_callable
=StringIO
) as stdout_mock
:
2320 portfolio
.ReportStore
.set_verbose(False)
2321 portfolio
.ReportStore
.print_log("Coucou")
2322 portfolio
.ReportStore
.print_log(portfolio
.Amount("BTC", 1))
2323 self
.assertEqual(stdout_mock
.getvalue(), "")
2325 def test_to_json(self
):
2326 portfolio
.ReportStore
.logs
.append({"foo": "bar"}
)
2327 self
.assertEqual('[{"foo": "bar"}]', portfolio
.ReportStore
.to_json())
2328 portfolio
.ReportStore
.logs
.append({"date": portfolio.datetime(2018, 2, 24)}
)
2329 self
.assertEqual('[{"foo": "bar"}, {"date": "2018-02-24T00:00:00"}]', portfolio
.ReportStore
.to_json())
2330 portfolio
.ReportStore
.logs
.append({"amount": portfolio.Amount("BTC", 1)}
)
2331 with self
.assertRaises(TypeError):
2332 portfolio
.ReportStore
.to_json()
2334 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2335 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2336 def test_log_stage(self
, add_log
, print_log
):
2337 portfolio
.ReportStore
.log_stage("foo")
2338 print_log
.assert_has_calls([
2339 mock
.call("-----------"),
2340 mock
.call("[Stage] foo"),
2342 add_log
.assert_called_once_with({'type': 'stage', 'stage': 'foo'}
)
2344 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2345 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2346 @mock.patch("store.BalanceStore")
2347 def test_log_balances(self
, balance_store
, add_log
, print_log
):
2348 balance_store
.as_json
.return_value
= "json"
2349 balance_store
.all
= { "FOO": "bar", "BAR": "baz" }
2351 portfolio
.ReportStore
.log_balances("market", tag
="tag")
2352 print_log
.assert_has_calls([
2353 mock
.call("[Balance]"),
2357 add_log
.assert_called_once_with({
2363 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2364 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2365 def test_log_tickers(self
, add_log
, print_log
):
2366 market
= mock
.Mock()
2368 "BTC": portfolio
.Amount("BTC", 10),
2369 "ETH": portfolio
.Amount("BTC", D("0.3"))
2371 amounts
["ETH"].rate
= D("0.1")
2373 portfolio
.ReportStore
.log_tickers(market
, amounts
, "BTC", "default", "total")
2374 print_log
.assert_not_called()
2375 add_log
.assert_called_once_with({
2377 'compute_value': 'default',
2378 'balance_type': 'total',
2391 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2392 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2393 def test_log_dispatch(self
, add_log
, print_log
):
2394 amount
= portfolio
.Amount("BTC", "10.3")
2396 "BTC": portfolio
.Amount("BTC", 10),
2397 "ETH": portfolio
.Amount("BTC", D("0.3"))
2399 portfolio
.ReportStore
.log_dispatch(amount
, amounts
, "medium", "repartition")
2400 print_log
.assert_not_called()
2401 add_log
.assert_called_once_with({
2403 'liquidity': 'medium',
2404 'repartition_ratio': 'repartition',
2415 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2416 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2417 def test_log_trades(self
, add_log
, print_log
):
2418 trade_mock1
= mock
.Mock()
2419 trade_mock2
= mock
.Mock()
2420 trade_mock1
.as_json
.return_value
= { "trade": "1" }
2421 trade_mock2
.as_json
.return_value
= { "trade": "2" }
2423 matching_and_trades
= [
2424 (True, trade_mock1
),
2425 (False, trade_mock2
),
2427 portfolio
.ReportStore
.log_trades(matching_and_trades
, "only", "debug")
2429 print_log
.assert_not_called()
2430 add_log
.assert_called_with({
2435 {'trade': '1', 'skipped': False}
,
2436 {'trade': '2', 'skipped': True}
2440 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2441 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2442 @mock.patch.object(portfolio
.TradeStore
, "print_all_with_order")
2443 def test_log_orders(self
, print_all_with_order
, add_log
, print_log
):
2444 order_mock1
= mock
.Mock()
2445 order_mock2
= mock
.Mock()
2447 order_mock1
.as_json
.return_value
= "order1"
2448 order_mock2
.as_json
.return_value
= "order2"
2450 orders
= [order_mock1
, order_mock2
]
2452 portfolio
.ReportStore
.log_orders(orders
, tick
="tick",
2453 only
="only", compute_value
="compute_value")
2455 print_log
.assert_called_once_with("[Orders]")
2456 print_all_with_order
.assert_called_once_with(ind
="\t")
2458 add_log
.assert_called_with({
2461 'compute_value': 'compute_value',
2463 'orders': ['order1', 'order2']
2466 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2467 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2468 def test_log_order(self
, add_log
, print_log
):
2469 order_mock
= mock
.Mock()
2470 order_mock
.as_json
.return_value
= "order"
2471 new_order_mock
= mock
.Mock()
2472 new_order_mock
.as_json
.return_value
= "new_order"
2473 order_mock
.__repr
__ = mock
.Mock()
2474 order_mock
.__repr
__.return_value
= "Order Mock"
2475 new_order_mock
.__repr
__ = mock
.Mock()
2476 new_order_mock
.__repr
__.return_value
= "New order Mock"
2478 with self
.subTest(finished
=True):
2479 portfolio
.ReportStore
.log_order(order_mock
, 1, finished
=True)
2480 print_log
.assert_called_once_with("[Order] Finished Order Mock")
2481 add_log
.assert_called_once_with({
2486 'compute_value': None,
2490 add_log
.reset_mock()
2491 print_log
.reset_mock()
2493 with self
.subTest(update
="waiting"):
2494 portfolio
.ReportStore
.log_order(order_mock
, 1, update
="waiting")
2495 print_log
.assert_called_once_with("[Order] Order Mock, tick 1, waiting")
2496 add_log
.assert_called_once_with({
2499 'update': 'waiting',
2501 'compute_value': None,
2505 add_log
.reset_mock()
2506 print_log
.reset_mock()
2507 with self
.subTest(update
="adjusting"):
2508 portfolio
.ReportStore
.log_order(order_mock
, 3,
2509 update
="adjusting", new_order
=new_order_mock
,
2510 compute_value
="default")
2511 print_log
.assert_called_once_with("[Order] Order Mock, tick 3, cancelling and adjusting to New order Mock")
2512 add_log
.assert_called_once_with({
2515 'update': 'adjusting',
2517 'compute_value': "default",
2518 'new_order': 'new_order'
2521 add_log
.reset_mock()
2522 print_log
.reset_mock()
2523 with self
.subTest(update
="market_fallback"):
2524 portfolio
.ReportStore
.log_order(order_mock
, 7,
2525 update
="market_fallback", new_order
=new_order_mock
)
2526 print_log
.assert_called_once_with("[Order] Order Mock, tick 7, fallbacking to market value")
2527 add_log
.assert_called_once_with({
2530 'update': 'market_fallback',
2532 'compute_value': None,
2533 'new_order': 'new_order'
2536 add_log
.reset_mock()
2537 print_log
.reset_mock()
2538 with self
.subTest(update
="market_adjusting"):
2539 portfolio
.ReportStore
.log_order(order_mock
, 17,
2540 update
="market_adjust", new_order
=new_order_mock
)
2541 print_log
.assert_called_once_with("[Order] Order Mock, tick 17, market value, cancelling and adjusting to New order Mock")
2542 add_log
.assert_called_once_with({
2545 'update': 'market_adjust',
2547 'compute_value': None,
2548 'new_order': 'new_order'
2551 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2552 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2553 def test_log_move_balances(self
, add_log
, print_log
):
2555 "BTC": portfolio
.Amount("BTC", 10),
2559 "BTC": portfolio
.Amount("BTC", 3),
2562 portfolio
.ReportStore
.log_move_balances(needed
, moving
, True)
2563 print_log
.assert_not_called()
2564 add_log
.assert_called_once_with({
2565 'type': 'move_balances',
2577 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2578 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2579 def test_log_http_request(self
, add_log
, print_log
):
2580 response
= mock
.Mock()
2581 response
.status_code
= 200
2582 response
.text
= "Hey"
2584 portfolio
.ReportStore
.log_http_request("method", "url", "body",
2585 "headers", response
)
2586 print_log
.assert_not_called()
2587 add_log
.assert_called_once_with({
2588 'type': 'http_request',
2592 'headers': 'headers',
2597 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2598 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2599 def test_log_error(self
, add_log
, print_log
):
2600 with self
.subTest(message
=None, exception
=None):
2601 portfolio
.ReportStore
.log_error("action")
2602 print_log
.assert_called_once_with("[Error] action")
2603 add_log
.assert_called_once_with({
2606 'exception_class': None,
2607 'exception_message': None,
2611 print_log
.reset_mock()
2612 add_log
.reset_mock()
2613 with self
.subTest(message
="Hey", exception
=None):
2614 portfolio
.ReportStore
.log_error("action", message
="Hey")
2615 print_log
.assert_has_calls([
2616 mock
.call("[Error] action"),
2619 add_log
.assert_called_once_with({
2622 'exception_class': None,
2623 'exception_message': None,
2627 print_log
.reset_mock()
2628 add_log
.reset_mock()
2629 with self
.subTest(message
=None, exception
=Exception("bouh")):
2630 portfolio
.ReportStore
.log_error("action", exception
=Exception("bouh"))
2631 print_log
.assert_has_calls([
2632 mock
.call("[Error] action"),
2633 mock
.call("\tException: bouh")
2635 add_log
.assert_called_once_with({
2638 'exception_class': "Exception",
2639 'exception_message': "bouh",
2643 print_log
.reset_mock()
2644 add_log
.reset_mock()
2645 with self
.subTest(message
="Hey", exception
=Exception("bouh")):
2646 portfolio
.ReportStore
.log_error("action", message
="Hey", exception
=Exception("bouh"))
2647 print_log
.assert_has_calls([
2648 mock
.call("[Error] action"),
2649 mock
.call("\tException: bouh"),
2652 add_log
.assert_called_once_with({
2655 'exception_class': "Exception",
2656 'exception_message': "bouh",
2660 @mock.patch.object(portfolio
.ReportStore
, "print_log")
2661 @mock.patch.object(portfolio
.ReportStore
, "add_log")
2662 def test_log_debug_action(self
, add_log
, print_log
):
2663 portfolio
.ReportStore
.log_debug_action("Hey")
2665 print_log
.assert_called_once_with("[Debug] Hey")
2666 add_log
.assert_called_once_with({
2667 'type': 'debug_action',
2671 @unittest.skipUnless("acceptance" in limits
, "Acceptance skipped")
2672 class AcceptanceTest(WebMockTestCase
):
2673 @unittest.expectedFailure
2674 def test_success_sell_only_necessary(self
):
2675 # FIXME: catch stdout
2676 portfolio
.ReportStore
.verbose_print
= False
2679 "exchange_free": D("1.0"),
2680 "exchange_used": D("0.0"),
2681 "exchange_total": D("1.0"),
2685 "exchange_free": D("4.0"),
2686 "exchange_used": D("0.0"),
2687 "exchange_total": D("4.0"),
2691 "exchange_free": D("1000.0"),
2692 "exchange_used": D("0.0"),
2693 "exchange_total": D("1000.0"),
2694 "total": D("1000.0"),
2698 "ETH": (D("0.25"), "long"),
2699 "ETC": (D("0.25"), "long"),
2700 "BTC": (D("0.4"), "long"),
2701 "BTD": (D("0.01"), "short"),
2702 "B2X": (D("0.04"), "long"),
2703 "USDT": (D("0.05"), "long"),
2706 def fetch_ticker(symbol
):
2707 if symbol
== "ETH/BTC":
2709 "symbol": "ETH/BTC",
2713 if symbol
== "ETC/BTC":
2715 "symbol": "ETC/BTC",
2719 if symbol
== "XVG/BTC":
2721 "symbol": "XVG/BTC",
2722 "bid": D("0.00003"),
2725 if symbol
== "BTD/BTC":
2727 "symbol": "BTD/BTC",
2731 if symbol
== "B2X/BTC":
2733 "symbol": "B2X/BTC",
2737 if symbol
== "USDT/BTC":
2738 raise helper
.ExchangeError
2739 if symbol
== "BTC/USDT":
2741 "symbol": "BTC/USDT",
2745 self
.fail("Shouldn't have been called with {}".format(symbol
))
2747 market
= mock
.Mock()
2748 market
.fetch_all_balances
.return_value
= fetch_balance
2749 market
.fetch_ticker
.side_effect
= fetch_ticker
2750 with mock
.patch
.object(portfolio
.Portfolio
, "repartition", return_value
=repartition
):
2752 helper
.prepare_trades(market
)
2754 balances
= portfolio
.BalanceStore
.all
2755 self
.assertEqual(portfolio
.Amount("ETH", 1), balances
["ETH"].total
)
2756 self
.assertEqual(portfolio
.Amount("ETC", 4), balances
["ETC"].total
)
2757 self
.assertEqual(portfolio
.Amount("XVG", 1000), balances
["XVG"].total
)
2760 trades
= portfolio
.TradeStore
.all
2761 self
.assertEqual(portfolio
.Amount("BTC", D("0.15")), trades
[0].value_from
)
2762 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
[0].value_to
)
2763 self
.assertEqual("dispose", trades
[0].action
)
2765 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
[1].value_from
)
2766 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
[1].value_to
)
2767 self
.assertEqual("acquire", trades
[1].action
)
2769 self
.assertEqual(portfolio
.Amount("BTC", D("0.04")), trades
[2].value_from
)
2770 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[2].value_to
)
2771 self
.assertEqual("dispose", trades
[2].action
)
2773 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[3].value_from
)
2774 self
.assertEqual(portfolio
.Amount("BTC", D("-0.002")), trades
[3].value_to
)
2775 self
.assertEqual("acquire", trades
[3].action
)
2777 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[4].value_from
)
2778 self
.assertEqual(portfolio
.Amount("BTC", D("0.008")), trades
[4].value_to
)
2779 self
.assertEqual("acquire", trades
[4].action
)
2781 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[5].value_from
)
2782 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
[5].value_to
)
2783 self
.assertEqual("acquire", trades
[5].action
)
2786 portfolio
.TradeStore
.prepare_orders(only
="dispose", compute_value
=lambda x
, y
: x
["bid"] * D("1.001"))
2788 all_orders
= portfolio
.TradeStore
.all_orders(state
="pending")
2789 self
.assertEqual(2, len(all_orders
))
2790 self
.assertEqual(2, 3*all_orders
[0].amount
.value
)
2791 self
.assertEqual(D("0.14014"), all_orders
[0].rate
)
2792 self
.assertEqual(1000, all_orders
[1].amount
.value
)
2793 self
.assertEqual(D("0.00003003"), all_orders
[1].rate
)
2796 def create_order(symbol
, type, action
, amount
, price
=None, account
="exchange"):
2797 self
.assertEqual("limit", type)
2798 if symbol
== "ETH/BTC":
2799 self
.assertEqual("sell", action
)
2800 self
.assertEqual(D('0.66666666'), amount
)
2801 self
.assertEqual(D("0.14014"), price
)
2802 elif symbol
== "XVG/BTC":
2803 self
.assertEqual("sell", action
)
2804 self
.assertEqual(1000, amount
)
2805 self
.assertEqual(D("0.00003003"), price
)
2807 self
.fail("I shouldn't have been called")
2812 market
.create_order
.side_effect
= create_order
2813 market
.order_precision
.return_value
= 8
2816 portfolio
.TradeStore
.run_orders()
2818 self
.assertEqual("open", all_orders
[0].status
)
2819 self
.assertEqual("open", all_orders
[1].status
)
2821 market
.fetch_order
.return_value
= { "status": "closed", "datetime": "2018-01-20 13:40:00" }
2822 market
.privatePostReturnOrderTrades
.return_value
= [
2824 "tradeID": 42, "type": "buy", "fee": "0.0015",
2825 "date": "2017-12-30 12:00:12", "rate": "0.1",
2826 "amount": "10", "total": "1"
2829 with mock
.patch
.object(portfolio
.time
, "sleep") as sleep
:
2831 helper
.follow_orders(verbose
=False)
2833 sleep
.assert_called_with(30)
2835 for order
in all_orders
:
2836 self
.assertEqual("closed", order
.status
)
2840 "exchange_free": D("1.0") / 3,
2841 "exchange_used": D("0.0"),
2842 "exchange_total": D("1.0") / 3,
2844 "total": D("1.0") / 3,
2847 "exchange_free": D("0.134"),
2848 "exchange_used": D("0.0"),
2849 "exchange_total": D("0.134"),
2851 "total": D("0.134"),
2854 "exchange_free": D("4.0"),
2855 "exchange_used": D("0.0"),
2856 "exchange_total": D("4.0"),
2861 "exchange_free": D("0.0"),
2862 "exchange_used": D("0.0"),
2863 "exchange_total": D("0.0"),
2868 market
.fetch_all_balances
.return_value
= fetch_balance
2870 with mock
.patch
.object(portfolio
.Portfolio
, "repartition", return_value
=repartition
):
2872 helper
.update_trades(market
, only
="acquire", compute_value
="average")
2874 balances
= portfolio
.BalanceStore
.all
2875 self
.assertEqual(portfolio
.Amount("ETH", 1 / D("3")), balances
["ETH"].total
)
2876 self
.assertEqual(portfolio
.Amount("ETC", 4), balances
["ETC"].total
)
2877 self
.assertEqual(portfolio
.Amount("BTC", D("0.134")), balances
["BTC"].total
)
2878 self
.assertEqual(portfolio
.Amount("XVG", 0), balances
["XVG"].total
)
2881 trades
= portfolio
.TradeStore
.all
2882 self
.assertEqual(portfolio
.Amount("BTC", D("0.15")), trades
[0].value_from
)
2883 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
[0].value_to
)
2884 self
.assertEqual("dispose", trades
[0].action
)
2886 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
[1].value_from
)
2887 self
.assertEqual(portfolio
.Amount("BTC", D("0.05")), trades
[1].value_to
)
2888 self
.assertEqual("acquire", trades
[1].action
)
2890 self
.assertNotIn("BTC", trades
)
2892 self
.assertEqual(portfolio
.Amount("BTC", D("0.04")), trades
[2].value_from
)
2893 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[2].value_to
)
2894 self
.assertEqual("dispose", trades
[2].action
)
2896 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[3].value_from
)
2897 self
.assertEqual(portfolio
.Amount("BTC", D("-0.002")), trades
[3].value_to
)
2898 self
.assertEqual("acquire", trades
[3].action
)
2900 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[4].value_from
)
2901 self
.assertEqual(portfolio
.Amount("BTC", D("0.008")), trades
[4].value_to
)
2902 self
.assertEqual("acquire", trades
[4].action
)
2904 self
.assertEqual(portfolio
.Amount("BTC", D("0.00")), trades
[5].value_from
)
2905 self
.assertEqual(portfolio
.Amount("BTC", D("0.01")), trades
[5].value_to
)
2906 self
.assertEqual("acquire", trades
[5].action
)
2909 portfolio
.TradeStore
.prepare_orders(only
="acquire", compute_value
=lambda x
, y
: x
["ask"])
2911 all_orders
= portfolio
.TradeStore
.all_orders(state
="pending")
2912 self
.assertEqual(4, len(all_orders
))
2913 self
.assertEqual(portfolio
.Amount("ETC", D("12.83333333")), round(all_orders
[0].amount
))
2914 self
.assertEqual(D("0.003"), all_orders
[0].rate
)
2915 self
.assertEqual("buy", all_orders
[0].action
)
2916 self
.assertEqual("long", all_orders
[0].trade_type
)
2918 self
.assertEqual(portfolio
.Amount("BTD", D("1.61666666")), round(all_orders
[1].amount
))
2919 self
.assertEqual(D("0.0012"), all_orders
[1].rate
)
2920 self
.assertEqual("sell", all_orders
[1].action
)
2921 self
.assertEqual("short", all_orders
[1].trade_type
)
2923 diff
= portfolio
.Amount("B2X", D("19.4")/3) - all_orders
[2].amount
2924 self
.assertAlmostEqual(0, diff
.value
)
2925 self
.assertEqual(D("0.0012"), all_orders
[2].rate
)
2926 self
.assertEqual("buy", all_orders
[2].action
)
2927 self
.assertEqual("long", all_orders
[2].trade_type
)
2929 self
.assertEqual(portfolio
.Amount("BTC", D("0.0097")), all_orders
[3].amount
)
2930 self
.assertEqual(D("16000"), all_orders
[3].rate
)
2931 self
.assertEqual("sell", all_orders
[3].action
)
2932 self
.assertEqual("long", all_orders
[3].trade_type
)
2936 # Move balances to margin
2940 # portfolio.TradeStore.run_orders()
2942 with mock
.patch
.object(portfolio
.time
, "sleep") as sleep
:
2944 helper
.follow_orders(verbose
=False)
2946 sleep
.assert_called_with(30)
2948 if __name__
== '__main__':