filled_amount += order.filled_amount(in_base_currency=in_base_currency)
return filled_amount
- def update_order(self, order, tick):
- actions = {
- 0: ["waiting", None],
- 1: ["waiting", None],
- 2: ["adjusting", lambda x, y: (x[y] + x["average"]) / 2],
- 3: ["waiting", None],
- 4: ["waiting", None],
- 5: ["adjusting", lambda x, y: (x[y]*2 + x["average"]) / 3],
- 6: ["waiting", None],
- 7: ["market_fallback", "default"],
- }
+ tick_actions = {
+ 0: ["waiting", None],
+ 1: ["waiting", None],
+ 2: ["adjusting", lambda x, y: (x[y] + x["average"]) / 2],
+ 3: ["waiting", None],
+ 4: ["waiting", None],
+ 5: ["adjusting", lambda x, y: (x[y]*2 + x["average"]) / 3],
+ 6: ["waiting", None],
+ 7: ["market_fallback", "default"],
+ }
- if tick in actions:
- update, compute_value = actions[tick]
+ def tick_actions_recreate(self, tick, default="average"):
+ return ([default] + \
+ [ y[1] for x, y in self.tick_actions.items() if x <= tick and y[1] is not None ])[-1]
+
+ def update_order(self, order, tick):
+ if tick in self.tick_actions:
+ update, compute_value = self.tick_actions[tick]
elif tick % 3 == 1:
update = "market_adjust"
compute_value = "default"
self.orders.append(order)
return order
- def reopen_same_order(self, order):
- new_order = Order(order.action, order.amount, order.rate,
- order.base_currency, order.trade_type, self.market,
- self, close_if_possible=order.close_if_possible)
- self.orders.append(new_order)
- new_order.run()
- return new_order
-
def as_json(self):
return {
"action": self.action,
self.fetch()
return self.status
- def fix_disappeared_order(self):
+ def mark_disappeared_order(self):
if self.status.startswith("closed") and \
- len(self.mouvements) == 1 and \
- self.mouvements[0].total_in_base == 0:
+ len(self.mouvements) > 0 and \
+ self.mouvements[-1].total_in_base == 0:
self.status = "error_disappeared"
- new_order = self.trade.reopen_same_order(self)
- self.market.report.log_error("fetch",
- message="Order {} disappeared, recreating it as {}".format(self, new_order))
def mark_finished_order(self):
if self.status.startswith("closed") and self.market.debug:
self.fetch_mouvements()
- self.fix_disappeared_order()
+ self.mark_disappeared_order()
self.mark_finished_order()
# FIXME: consider open order with dust remaining as closed
for mouvement_hash in mouvements:
self.mouvements.append(Mouvement(self.amount.currency,
self.base_currency, mouvement_hash))
+ self.mouvements.sort(key= lambda x: x.date)
def cancel(self):
if self.market.debug:
else:
time_mock.assert_called_with(sleep)
+ with self.subTest("disappearing order"), \
+ mock.patch("market.ReportStore"):
+ all_orders.reset_mock()
+ m = market.Market(self.ccxt, self.market_args())
+
+ order_mock1 = mock.Mock()
+ order_mock2 = mock.Mock()
+ all_orders.side_effect = [
+ [order_mock1, order_mock2],
+ [order_mock1, order_mock2],
+
+ [order_mock1, order_mock2],
+ [order_mock1, order_mock2],
+
+ []
+ ]
+
+ order_mock1.get_status.side_effect = ["open", "closed"]
+ order_mock2.get_status.side_effect = ["open", "error_disappeared"]
+
+ order_mock1.trade = mock.Mock()
+ trade_mock = mock.Mock()
+ order_mock2.trade = trade_mock
+
+ trade_mock.tick_actions_recreate.return_value = "tick1"
+
+ m.follow_orders()
+
+ trade_mock.tick_actions_recreate.assert_called_once_with(2)
+ trade_mock.prepare_order.assert_called_once_with(compute_value="tick1")
+ m.report.log_error.assert_called_once_with("follow_orders", message=mock.ANY)
+
@mock.patch.object(market.BalanceStore, "fetch_balances")
def test_move_balance(self, fetch_balances):
for debug in [True, False]:
order1.filled_amount.assert_called_with(in_base_currency=True)
order2.filled_amount.assert_called_with(in_base_currency=True)
- def test_reopen_same_order(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)
- order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
- D("0.1"), "BTC", "long", self.m, "trade")
- with mock.patch("portfolio.Order.run") as run:
- new_order = trade.reopen_same_order(order)
- self.assertEqual("buy", new_order.action)
- self.assertEqual(portfolio.Amount("ETH", 10), new_order.amount)
- self.assertEqual(D("0.1"), new_order.rate)
- self.assertEqual("BTC", new_order.base_currency)
- self.assertEqual("long", new_order.trade_type)
- self.assertEqual(self.m, new_order.market)
- self.assertEqual(False, new_order.close_if_possible)
- self.assertEqual(trade, new_order.trade)
- run.assert_called_once()
- self.assertEqual(1, len(trade.orders))
- self.assertEqual(new_order, trade.orders[0])
-
@mock.patch.object(portfolio.Computation, "compute_value")
@mock.patch.object(portfolio.Trade, "filled_amount")
@mock.patch.object(portfolio, "Order")
D("125"), "FOO", "long", self.m,
trade, close_if_possible=False)
+ def test_tick_actions_recreate(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)
+
+ self.assertEqual("average", trade.tick_actions_recreate(0))
+ self.assertEqual("foo", trade.tick_actions_recreate(0, default="foo"))
+ self.assertEqual("average", trade.tick_actions_recreate(1))
+ self.assertEqual(trade.tick_actions[2][1], trade.tick_actions_recreate(2))
+ self.assertEqual(trade.tick_actions[2][1], trade.tick_actions_recreate(3))
+ self.assertEqual(trade.tick_actions[5][1], trade.tick_actions_recreate(5))
+ self.assertEqual(trade.tick_actions[5][1], trade.tick_actions_recreate(6))
+ self.assertEqual("default", trade.tick_actions_recreate(7))
+ self.assertEqual("default", trade.tick_actions_recreate(8))
@mock.patch.object(portfolio.Trade, "prepare_order")
def test_update_order(self, prepare_order):
self.m.ccxt.privatePostReturnOrderTrades.return_value = [
{
"tradeID": 42, "type": "buy", "fee": "0.0015",
- "date": "2017-12-30 12:00:12", "rate": "0.1",
+ "date": "2017-12-30 13:00:12", "rate": "0.1",
"amount": "3", "total": "0.3"
},
{
"tradeID": 43, "type": "buy", "fee": "0.0015",
- "date": "2017-12-30 13:00:12", "rate": "0.2",
+ "date": "2017-12-30 12:00:12", "rate": "0.2",
"amount": "2", "total": "0.4"
}
]
self.m.ccxt.privatePostReturnOrderTrades.assert_called_with({"orderNumber": 12})
self.assertEqual(2, len(order.mouvements))
- self.assertEqual(42, order.mouvements[0].id)
- self.assertEqual(43, order.mouvements[1].id)
+ self.assertEqual(43, order.mouvements[0].id)
+ self.assertEqual(42, order.mouvements[1].id)
self.m.ccxt.privatePostReturnOrderTrades.side_effect = portfolio.ExchangeError
order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
self.m.report.log_debug_action.assert_called_once()
@mock.patch.object(portfolio.Order, "fetch_mouvements")
- @mock.patch.object(portfolio.Order, "fix_disappeared_order")
+ @mock.patch.object(portfolio.Order, "mark_disappeared_order")
@mock.patch.object(portfolio.Order, "mark_finished_order")
- def test_fetch(self, mark_finished_order, fix_disappeared_order, fetch_mouvements):
+ def test_fetch(self, mark_finished_order, mark_disappeared_order, fetch_mouvements):
order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
D("0.1"), "BTC", "long", self.m, "trade")
order.id = 45
self.m.report.log_debug_action.reset_mock()
self.m.ccxt.fetch_order.assert_not_called()
mark_finished_order.assert_not_called()
- fix_disappeared_order.assert_not_called()
+ mark_disappeared_order.assert_not_called()
fetch_mouvements.assert_not_called()
with self.subTest(debug=False):
self.assertEqual(1, len(order.results))
self.m.report.log_debug_action.assert_not_called()
mark_finished_order.assert_called_once()
- fix_disappeared_order.assert_called_once()
+ mark_disappeared_order.assert_called_once()
mark_finished_order.reset_mock()
with self.subTest(missing_order=True):
self.assertEqual("closed_unknown", order.status)
mark_finished_order.assert_called_once()
- def test_fix_disappeared_order(self):
+ def test_mark_disappeared_order(self):
with self.subTest("Open order"):
order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
D("0.1"), "BTC", "long", self.m, "trade")
"fee":"0.00150000",
"date":"2018-04-02 00:09:13"
}))
- with mock.patch.object(order, "trade") as trade:
- order.fix_disappeared_order()
- trade.reopen_same_order.assert_not_called()
+ order.mark_disappeared_order()
+ self.assertEqual("pending", order.status)
with self.subTest("Non-zero amount"):
order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
"fee":"0.00150000",
"date":"2018-04-02 00:09:13"
}))
- with mock.patch.object(order, "trade") as trade:
- order.fix_disappeared_order()
- self.assertEqual("closed", order.status)
- trade.reopen_same_order.assert_not_called()
+ order.mark_disappeared_order()
+ self.assertEqual("closed", order.status)
with self.subTest("Other mouvements"):
order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
"fee":"0.00150000",
"date":"2018-04-02 00:09:13"
}))
- with mock.patch.object(order, "trade") as trade:
- order.fix_disappeared_order()
- self.assertEqual("closed", order.status)
- trade.reopen_same_order.assert_not_called()
+ order.mark_disappeared_order()
+ self.assertEqual("error_disappeared", order.status)
with self.subTest("Order disappeared"):
order = portfolio.Order("buy", portfolio.Amount("ETH", 10),
"fee":"0.00150000",
"date":"2018-04-02 00:09:13"
}))
- with mock.patch.object(order, "trade") as trade:
- trade.reopen_same_order.return_value = "New order"
- order.fix_disappeared_order()
- self.assertEqual("error_disappeared", order.status)
- trade.reopen_same_order.assert_called_once_with(order)
- self.m.report.log_error.assert_called_once_with('fetch',
- message='Order Order(buy long 10.00000000 ETH at 0.1 BTC [error_disappeared]) disappeared, recreating it as New order')
+ order.mark_disappeared_order()
+ self.assertEqual("error_disappeared", order.status)
@mock.patch.object(portfolio.Order, "fetch")
def test_get_status(self, fetch):