mock.patch.multiple(portfolio.Portfolio, last_date=None, data=None, liquidities={}),
mock.patch.multiple(portfolio.Computation,
computations=portfolio.Computation.computations),
- mock.patch.multiple(market.Market,
- fees_cache={},
- ticker_cache={},
- ticker_cache_timestamp=self.time.time()),
]
for patcher in self.patchers:
patcher.start()
"exchange_free": "0.35",
"exchange_used": "0.30",
"margin_total": "-10",
- "margin_borrowed": "-10",
- "margin_free": "0",
+ "margin_borrowed": "10",
+ "margin_available": "0",
+ "margin_in_position": "0",
"margin_position_type": "short",
"margin_borrowed_base_currency": "USDT",
"margin_liquidation_price": "1.20",
self.assertEqual("BTC", balance.exchange_total.currency)
self.assertEqual(portfolio.D("-10"), balance.margin_total.value)
- self.assertEqual(portfolio.D("-10"), balance.margin_borrowed.value)
- self.assertEqual(portfolio.D("0"), balance.margin_free.value)
+ self.assertEqual(portfolio.D("10"), balance.margin_borrowed.value)
+ self.assertEqual(portfolio.D("0"), balance.margin_available.value)
self.assertEqual("BTC", balance.margin_total.currency)
self.assertEqual("BTC", balance.margin_borrowed.currency)
- self.assertEqual("BTC", balance.margin_free.currency)
+ self.assertEqual("BTC", balance.margin_available.currency)
self.assertEqual("BTC", balance.currency)
self.assertEqual("Balance(BTX Exch: [❌1.00000000 BTX])", repr(balance))
balance = portfolio.Balance("BTX", { "margin_total": 3,
- "margin_borrowed": 1, "margin_free": 2 })
- self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + borrowed 1.00000000 BTX = 3.00000000 BTX])", repr(balance))
+ "margin_in_position": 1, "margin_available": 2 })
+ self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX + ❌1.00000000 BTX = 3.00000000 BTX])", repr(balance))
- balance = portfolio.Balance("BTX", { "margin_total": 2, "margin_free": 2 })
+ balance = portfolio.Balance("BTX", { "margin_total": 2, "margin_available": 2 })
self.assertEqual("Balance(BTX Margin: [✔2.00000000 BTX])", repr(balance))
balance = portfolio.Balance("BTX", { "margin_total": -3,
self.assertEqual("Balance(BTX Margin: [-3.00000000 BTX @@ 0.10000000 BTC/0.00200000 BTC])", repr(balance))
balance = portfolio.Balance("BTX", { "margin_total": 1,
- "margin_borrowed": 1, "exchange_free": 2, "exchange_total": 2})
- self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX] Margin: [borrowed 1.00000000 BTX] Total: [0.00000000 BTX])", repr(balance))
+ "margin_in_position": 1, "exchange_free": 2, "exchange_total": 2})
+ self.assertEqual("Balance(BTX Exch: [✔2.00000000 BTX] Margin: [❌1.00000000 BTX] Total: [0.00000000 BTX])", repr(balance))
def test_as_json(self):
balance = portfolio.Balance("BTX", { "exchange_free": 2, "exchange_total": 2 })
self.assertEqual(D(2), as_json["exchange_free"])
self.assertEqual(D(0), as_json["exchange_used"])
self.assertEqual(D(0), as_json["margin_total"])
- self.assertEqual(D(0), as_json["margin_free"])
+ self.assertEqual(D(0), as_json["margin_available"])
self.assertEqual(D(0), as_json["margin_borrowed"])
@unittest.skipUnless("unit" in limits, "Unit skipped")
m = market.Market.from_config({"key": "key", "secred": "secret"}, debug=True)
self.assertEqual(True, m.debug)
- def test_get_ticker(self):
- m = market.Market(self.ccxt)
- self.ccxt.fetch_ticker.side_effect = [
- { "bid": 1, "ask": 3 },
- market.ExchangeError("foo"),
- { "bid": 10, "ask": 40 },
- market.ExchangeError("foo"),
- market.ExchangeError("foo"),
+ def test_get_tickers(self):
+ self.ccxt.fetch_tickers.side_effect = [
+ "tickers",
+ market.NotSupported
]
- ticker = m.get_ticker("ETH", "ETC")
- self.ccxt.fetch_ticker.assert_called_with("ETH/ETC")
- self.assertEqual(1, ticker["bid"])
- self.assertEqual(3, ticker["ask"])
- self.assertEqual(2, ticker["average"])
- self.assertFalse(ticker["inverted"])
-
- ticker = m.get_ticker("ETH", "XVG")
- self.assertEqual(0.0625, ticker["average"])
- self.assertTrue(ticker["inverted"])
- self.assertIn("original", ticker)
- self.assertEqual(10, ticker["original"]["bid"])
-
- ticker = m.get_ticker("XVG", "XMR")
- self.assertIsNone(ticker)
-
- self.ccxt.fetch_ticker.assert_has_calls([
- mock.call("ETH/ETC"),
- mock.call("ETH/XVG"),
- mock.call("XVG/ETH"),
- mock.call("XVG/XMR"),
- mock.call("XMR/XVG"),
- ])
+ m = market.Market(self.ccxt)
+ self.assertEqual("tickers", m.get_tickers())
+ self.assertEqual("tickers", m.get_tickers())
+ self.ccxt.fetch_tickers.assert_called_once()
- self.ccxt = mock.Mock(spec=market.ccxt.poloniexE)
- m1b = market.Market(self.ccxt)
- m1b.get_ticker("ETH", "ETC")
- self.ccxt.fetch_ticker.assert_not_called()
-
- self.ccxt = mock.Mock(spec=market.ccxt.poloniex)
- m2 = market.Market(self.ccxt)
- self.ccxt.fetch_ticker.side_effect = [
- { "bid": 1, "ask": 3 },
- { "bid": 1.2, "ask": 3.5 },
- ]
- ticker1 = m2.get_ticker("ETH", "ETC")
- ticker2 = m2.get_ticker("ETH", "ETC")
- ticker3 = m2.get_ticker("ETC", "ETH")
- self.ccxt.fetch_ticker.assert_called_once_with("ETH/ETC")
- self.assertEqual(1, ticker1["bid"])
- self.assertDictEqual(ticker1, ticker2)
- self.assertDictEqual(ticker1, ticker3["original"])
-
- ticker4 = m2.get_ticker("ETH", "ETC", refresh=True)
- ticker5 = m2.get_ticker("ETH", "ETC")
- self.assertEqual(1.2, ticker4["bid"])
- self.assertDictEqual(ticker4, ticker5)
-
- self.ccxt = mock.Mock(spec=market.ccxt.binance)
- m3 = market.Market(self.ccxt)
- self.ccxt.fetch_ticker.side_effect = [
- { "bid": 1, "ask": 3 },
- { "bid": 1.2, "ask": 3.5 },
- ]
- ticker6 = m3.get_ticker("ETH", "ETC")
- m3.ticker_cache_timestamp -= 4
- ticker7 = m3.get_ticker("ETH", "ETC")
- m3.ticker_cache_timestamp -= 2
- ticker8 = m3.get_ticker("ETH", "ETC")
- self.assertDictEqual(ticker6, ticker7)
- self.assertEqual(1.2, ticker8["bid"])
+ self.assertIsNone(m.get_tickers(refresh=self.time.time()))
+
+ def test_get_ticker(self):
+ with self.subTest(get_tickers=True):
+ self.ccxt.fetch_tickers.return_value = {
+ "ETH/ETC": { "bid": 1, "ask": 3 },
+ "XVG/ETH": { "bid": 10, "ask": 40 },
+ }
+ m = market.Market(self.ccxt)
+
+ ticker = m.get_ticker("ETH", "ETC")
+ self.assertEqual(1, ticker["bid"])
+ self.assertEqual(3, ticker["ask"])
+ self.assertEqual(2, ticker["average"])
+ self.assertFalse(ticker["inverted"])
+
+ ticker = m.get_ticker("ETH", "XVG")
+ self.assertEqual(0.0625, ticker["average"])
+ self.assertTrue(ticker["inverted"])
+ self.assertIn("original", ticker)
+ self.assertEqual(10, ticker["original"]["bid"])
+
+ ticker = m.get_ticker("XVG", "XMR")
+ self.assertIsNone(ticker)
+
+ with self.subTest(get_tickers=False):
+ self.ccxt.fetch_tickers.return_value = None
+ self.ccxt.fetch_ticker.side_effect = [
+ { "bid": 1, "ask": 3 },
+ market.ExchangeError("foo"),
+ { "bid": 10, "ask": 40 },
+ market.ExchangeError("foo"),
+ market.ExchangeError("foo"),
+ ]
+
+ m = market.Market(self.ccxt)
+
+ ticker = m.get_ticker("ETH", "ETC")
+ self.ccxt.fetch_ticker.assert_called_with("ETH/ETC")
+ self.assertEqual(1, ticker["bid"])
+ self.assertEqual(3, ticker["ask"])
+ self.assertEqual(2, ticker["average"])
+ self.assertFalse(ticker["inverted"])
+
+ ticker = m.get_ticker("ETH", "XVG")
+ self.assertEqual(0.0625, ticker["average"])
+ self.assertTrue(ticker["inverted"])
+ self.assertIn("original", ticker)
+ self.assertEqual(10, ticker["original"]["bid"])
+
+ ticker = m.get_ticker("XVG", "XMR")
+ self.assertIsNone(ticker)
def test_fetch_fees(self):
m = market.Market(self.ccxt)
trade3 = portfolio.Trade(value_from, value_to, "XVG", m)
m.trades.all = [trade1, trade2, trade3]
- balance1 = portfolio.Balance("BTC", { "margin_free": "0" })
- balance2 = portfolio.Balance("USDT", { "margin_free": "100" })
- balance3 = portfolio.Balance("ETC", { "margin_free": "10" })
+ balance1 = portfolio.Balance("BTC", { "margin_in_position": "0", "margin_available": "0" })
+ balance2 = portfolio.Balance("USDT", { "margin_in_position": "100", "margin_available": "50" })
+ balance3 = portfolio.Balance("ETC", { "margin_in_position": "10", "margin_available": "15" })
m.balances.all = {"BTC": balance1, "USDT": balance2, "ETC": balance3}
m.move_balances()
self.assertEqual(3, m.report.log_debug_action.call_count)
else:
self.ccxt.transfer_balance.assert_any_call("BTC", 3, "exchange", "margin")
- self.ccxt.transfer_balance.assert_any_call("USDT", 50, "margin", "exchange")
- self.ccxt.transfer_balance.assert_any_call("ETC", 10, "margin", "exchange")
+ self.ccxt.transfer_balance.assert_any_call("USDT", 100, "exchange", "margin")
+ self.ccxt.transfer_balance.assert_any_call("ETC", 5, "margin", "exchange")
@unittest.skipUnless("unit" in limits, "Unit skipped")
class TradeStoreTest(WebMockTestCase):
trade_mock1 = mock.Mock()
trade_mock2 = mock.Mock()
+ trade_mock3 = mock.Mock()
trade_mock1.prepare_order.return_value = 1
trade_mock2.prepare_order.return_value = 2
+ trade_mock3.prepare_order.return_value = 3
+
+ trade_mock1.is_fullfiled = False
+ trade_mock2.is_fullfiled = False
+ trade_mock3.is_fullfiled = True
trade_store.all.append(trade_mock1)
trade_store.all.append(trade_mock2)
+ trade_store.all.append(trade_mock3)
trade_store.prepare_orders()
trade_mock1.prepare_order.assert_called_with(compute_value="default")
trade_mock2.prepare_order.assert_called_with(compute_value="default")
+ trade_mock3.prepare_order.assert_not_called()
self.m.report.log_orders.assert_called_once_with([1, 2], None, "default")
self.m.report.log_orders.reset_mock()
order_mock2.get_status.assert_called()
order_mock3.get_status.assert_called()
+ def test_pending(self):
+ trade_mock1 = mock.Mock()
+ trade_mock1.is_fullfiled = False
+ trade_mock2 = mock.Mock()
+ trade_mock2.is_fullfiled = False
+ trade_mock3 = mock.Mock()
+ trade_mock3.is_fullfiled = True
+
+ trade_store = market.TradeStore(self.m)
+
+ trade_store.all.append(trade_mock1)
+ trade_store.all.append(trade_mock2)
+ trade_store.all.append(trade_mock3)
+
+ self.assertEqual([trade_mock1, trade_mock2], trade_store.pending)
@unittest.skipUnless("unit" in limits, "Unit skipped")
class BalanceStoreTest(WebMockTestCase):
self.assertEqual(self.m, trade.market)
with self.assertRaises(AssertionError):
- portfolio.Trade(value_from, value_to, "ETC", self.m)
+ portfolio.Trade(value_from, -value_to, "ETH", self.m)
with self.assertRaises(AssertionError):
- value_from.linked_to = None
- portfolio.Trade(value_from, value_to, "ETH", self.m)
+ portfolio.Trade(value_from, value_to, "ETC", self.m)
with self.assertRaises(AssertionError):
value_from.currency = "ETH"
portfolio.Trade(value_from, value_to, "ETH", self.m)
+ value_from.currency = "BTC"
+ with self.assertRaises(AssertionError):
+ value_from2 = portfolio.Amount("BTC", "1.0")
+ portfolio.Trade(value_from2, value_to, "ETH", self.m)
value_from = portfolio.Amount("BTC", 0)
trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
self.assertEqual("short", trade.trade_type)
+ def test_is_fullfiled(self):
+ value_from = portfolio.Amount("BTC", "0.5")
+ value_from.linked_to = portfolio.Amount("ETH", "10.0")
+ value_to = portfolio.Amount("BTC", "1.0")
+ trade = portfolio.Trade(value_from, value_to, "ETH", self.m)
+
+ order1 = mock.Mock()
+ order1.filled_amount.return_value = portfolio.Amount("BTC", "0.3")
+
+ order2 = mock.Mock()
+ order2.filled_amount.return_value = portfolio.Amount("BTC", "0.01")
+ trade.orders.append(order1)
+ trade.orders.append(order2)
+
+ self.assertFalse(trade.is_fullfiled)
+
+ order3 = mock.Mock()
+ order3.filled_amount.return_value = portfolio.Amount("BTC", "0.19")
+ trade.orders.append(order3)
+
+ self.assertTrue(trade.is_fullfiled)
+
def test_filled_amount(self):
value_from = portfolio.Amount("BTC", "0.5")
value_from.linked_to = portfolio.Amount("ETH", "10.0")
self.assertEqual(2, self.m.report.log_order.call_count)
calls = [
mock.call(order_mock, 2, update="adjusting",
- compute_value='lambda x, y: (x[y] + x["average"]) / 2',
+ compute_value=mock.ANY,
new_order=new_order_mock),
mock.call(order_mock, 2, new_order=new_order_mock),
]
self.m.report.log_order.assert_called()
calls = [
mock.call(order_mock, 5, update="adjusting",
- compute_value='lambda x, y: (x[y]*2 + x["average"]) / 3',
+ compute_value=mock.ANY,
new_order=new_order_mock),
mock.call(order_mock, 5, new_order=new_order_mock),
]
order.cancel()
self.m.ccxt.cancel_order.assert_called_with(42)
- fetch.assert_called_once_with(force=True)
+ fetch.assert_called_once_with()
self.m.report.log_debug_action.assert_not_called()
def test_dust_amount_remaining(self):
D("0.1"), "BTC", "long", self.m, "trade")
self.assertEqual(9, order.remaining_amount().value)
- order.fetch.assert_not_called()
order.status = "open"
self.assertEqual(9, order.remaining_amount().value)
- fetch.assert_called_once()
@mock.patch.object(portfolio.Order, "fetch")
def test_filled_amount(self, fetch):
@mock.patch.object(portfolio.Order, "fetch_mouvements")
def test_fetch(self, fetch_mouvements):
- time = self.time.time()
- with mock.patch.object(portfolio.time, "time") as time_mock:
- order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
- D("0.1"), "BTC", "long", self.m, "trade")
- order.id = 45
- with self.subTest(debug=True):
- self.m.debug = True
- order.fetch()
- time_mock.assert_not_called()
- self.m.report.log_debug_action.assert_called_once()
- self.m.report.log_debug_action.reset_mock()
- order.fetch(force=True)
- time_mock.assert_not_called()
- self.m.ccxt.fetch_order.assert_not_called()
- fetch_mouvements.assert_not_called()
- self.m.report.log_debug_action.assert_called_once()
- self.m.report.log_debug_action.reset_mock()
- self.assertIsNone(order.fetch_cache_timestamp)
-
- with self.subTest(debug=False):
- self.m.debug = False
- time_mock.return_value = time
- self.m.ccxt.fetch_order.return_value = {
- "status": "foo",
- "datetime": "timestamp"
- }
- order.fetch()
-
- self.m.ccxt.fetch_order.assert_called_once_with(45, symbol="ETH")
- fetch_mouvements.assert_called_once()
- self.assertEqual("foo", order.status)
- self.assertEqual("timestamp", order.timestamp)
- self.assertEqual(time, order.fetch_cache_timestamp)
- self.assertEqual(1, len(order.results))
-
- self.m.ccxt.fetch_order.reset_mock()
- fetch_mouvements.reset_mock()
-
- time_mock.return_value = time + 8
- order.fetch()
- self.m.ccxt.fetch_order.assert_not_called()
- fetch_mouvements.assert_not_called()
+ order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
+ D("0.1"), "BTC", "long", self.m, "trade")
+ order.id = 45
+ with self.subTest(debug=True):
+ self.m.debug = True
+ order.fetch()
+ self.m.report.log_debug_action.assert_called_once()
+ self.m.report.log_debug_action.reset_mock()
+ self.m.ccxt.fetch_order.assert_not_called()
+ fetch_mouvements.assert_not_called()
- order.fetch(force=True)
- self.m.ccxt.fetch_order.assert_called_once_with(45, symbol="ETH")
- fetch_mouvements.assert_called_once()
+ with self.subTest(debug=False):
+ self.m.debug = False
+ self.m.ccxt.fetch_order.return_value = {
+ "status": "foo",
+ "datetime": "timestamp"
+ }
+ order.fetch()
- self.m.ccxt.fetch_order.reset_mock()
- fetch_mouvements.reset_mock()
+ self.m.ccxt.fetch_order.assert_called_once_with(45, symbol="ETH")
+ fetch_mouvements.assert_called_once()
+ self.assertEqual("foo", order.status)
+ self.assertEqual("timestamp", order.timestamp)
+ self.assertEqual(1, len(order.results))
+ self.m.report.log_debug_action.assert_not_called()
- time_mock.return_value = time + 19
+ with self.subTest(missing_order=True):
+ self.m.ccxt.fetch_order.side_effect = [
+ portfolio.OrderNotCached,
+ ]
order.fetch()
- self.m.ccxt.fetch_order.assert_called_once_with(45, symbol="ETH")
- fetch_mouvements.assert_called_once()
- self.m.report.log_debug_action.assert_not_called()
+ self.assertEqual("closed_unknown", order.status)
@mock.patch.object(portfolio.Order, "fetch")
@mock.patch.object(portfolio.Order, "mark_finished_order")
'total': D('10.3')
})
+ add_log.reset_mock()
+ compute_value = lambda x: x["bid"]
+ report_store.log_tickers(amounts, "BTC", compute_value, "total")
+ add_log.assert_called_once_with({
+ 'type': 'tickers',
+ 'compute_value': 'compute_value = lambda x: x["bid"]',
+ 'balance_type': 'total',
+ 'currency': 'BTC',
+ 'balances': {
+ 'BTC': D('10'),
+ 'ETH': D('0.3')
+ },
+ 'rates': {
+ 'BTC': None,
+ 'ETH': D('0.1')
+ },
+ 'total': D('10.3')
+ })
+
@mock.patch.object(market.ReportStore, "print_log")
@mock.patch.object(market.ReportStore, "add_log")
def test_log_dispatch(self, add_log, print_log):
'orders': ['order1', 'order2']
})
+ add_log.reset_mock()
+ def compute_value(x, y):
+ return x[y]
+ report_store.log_orders(orders, tick="tick",
+ only="only", compute_value=compute_value)
+ add_log.assert_called_with({
+ 'type': 'orders',
+ 'only': 'only',
+ 'compute_value': 'def compute_value(x, y):\n return x[y]',
+ 'tick': 'tick',
+ 'orders': ['order1', 'order2']
+ })
+
+
@mock.patch.object(market.ReportStore, "print_log")
@mock.patch.object(market.ReportStore, "add_log")
def test_log_order(self, add_log, print_log):
add_log.reset_mock()
print_log.reset_mock()
with self.subTest(update="adjusting"):
+ compute_value = lambda x: (x["bid"] + x["ask"]*2)/3
report_store.log_order(order_mock, 3,
update="adjusting", new_order=new_order_mock,
- compute_value="default")
+ compute_value=compute_value)
print_log.assert_called_once_with("[Order] Order Mock, tick 3, cancelling and adjusting to New order Mock")
add_log.assert_called_once_with({
'type': 'order',
'tick': 3,
'update': 'adjusting',
'order': 'order',
- 'compute_value': "default",
+ 'compute_value': 'compute_value = lambda x: (x["bid"] + x["ask"]*2)/3',
'new_order': 'new_order'
})