Repository: liampauling/flumine
Pull request #389 (2021-03-08 09:51:27)

prevent closure functions being called on a recorder closure (dict no…
…t objects)

reorder process so closure called after market is added


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index a1dba32..8c33548 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,18 @@
 Release History
 ---------------
 
+1.17.8 (2021-03-08)
++++++++++++++++++++
+
+**Improvements**
+
+- Allow kwargs to be passed to `trade.create_order`
+- Correct handling off completed offset orderrs
+
+**Bug Fixes**
+
+- Prevent closure functions being called on a recorder closure
+
 1.17.7 (2021-03-05)
 +++++++++++++++++++
 
diff --git a/flumine/__version__.py b/flumine/__version__.py
index f1ca466..66cc33b 100644
--- a/flumine/__version__.py
+++ b/flumine/__version__.py
@@ -1,6 +1,6 @@
 __title__ = "flumine"
 __description__ = "Betfair trading framework"
 __url__ = "https://github.com/liampauling/flumine"
-__version__ = "1.17.7"
+__version__ = "1.17.8"
 __author__ = "Liam Pauling"
 __license__ = "MIT"
diff --git a/flumine/baseflumine.py b/flumine/baseflumine.py
index cad54ba..c9c8768 100644
--- a/flumine/baseflumine.py
+++ b/flumine/baseflumine.py
@@ -129,16 +129,16 @@ class BaseFlumine:
                         },
                     )
 
-            if market_book.status == "CLOSED":
-                self.handler_queue.put(events.CloseMarketEvent(market_book))
-                continue
-
             market = self.markets.markets.get(market_id)
             if market is None:
                 market = self._add_market(market_id, market_book)
             elif market.closed:
                 self.markets.add_market(market_id, market)
 
+            if market_book.status == "CLOSED":
+                self.handler_queue.put(events.CloseMarketEvent(market_book))
+                continue
+
             # process market
             market(market_book)
 
@@ -230,9 +230,11 @@ class BaseFlumine:
     def _process_close_market(self, event: events.CloseMarketEvent) -> None:
         market_book = event.event
         if isinstance(market_book, dict):
+            recorder = True
             market_id = market_book["id"]
             stream_id = market_book["_stream_id"]
         else:
+            recorder = False
             market_id = market_book.market_id
             stream_id = market_book.streaming_unique_id
         market = self.markets.markets.get(market_id)
@@ -242,17 +244,20 @@ class BaseFlumine:
                 extra={"market_id": market_id, **self.info},
             )
             return
-        market.close_market()
-        market.blotter.process_closed_market(event.event)
+        if market.closed is False:
+            market.close_market()
+        if recorder is False:
+            market.blotter.process_closed_market(event.event)
 
         for strategy in self.strategies:
             if stream_id in strategy.stream_ids:
                 strategy.process_closed_market(market, event.event)
 
-        if self.BACKTEST or self.client.paper_trade:
-            self._process_cleared_orders(events.ClearedOrdersEvent(market))
-        else:
-            self.cleared_market_queue.put(market_id)
+        if recorder is False:
+            if self.BACKTEST or self.client.paper_trade:
+                self._process_cleared_orders(events.ClearedOrdersEvent(market))
+            else:
+                self.cleared_market_queue.put(market_id)
         self.log_control(event)
         logger.info("Market closed", extra={"market_id": market_id, **self.info})
 
diff --git a/flumine/order/trade.py b/flumine/order/trade.py
index 8cbd3de..c0d5923 100644
--- a/flumine/order/trade.py
+++ b/flumine/order/trade.py
@@ -74,8 +74,9 @@ class Trade:
     def complete(self) -> bool:
         if self.status != TradeStatus.LIVE:
             return False
-        if self.offset_orders:
-            return False
+        for order in self.offset_orders:
+            if not order.complete:
+                return False
         for order in self.orders:
             if not order.complete:
                 return False
@@ -87,12 +88,15 @@ class Trade:
         order_type: Union[LimitOrder, LimitOnCloseOrder, MarketOnCloseOrder],
         handicap: float = 0,
         order: Type[BetfairOrder] = BetfairOrder,
+        **kwargs,
     ) -> BetfairOrder:
         if order_type.EXCHANGE != order.EXCHANGE:
             raise OrderError(
                 "Incorrect order/order_type exchange combination for trade.create_order"
             )
-        order = order(trade=self, side=side, order_type=order_type, handicap=handicap)
+        order = order(
+            trade=self, side=side, order_type=order_type, handicap=handicap, **kwargs
+        )
         self.orders.append(order)
         return order
 
@@ -157,6 +161,7 @@ class Trade:
             "place_reset_seconds": self.place_reset_seconds,
             "reset_seconds": self.reset_seconds,
             "orders": [o.id for o in self.orders],
+            "offset_orders": [o.id for o in self.offset_orders],
             "notes": self.notes_str,
             "market_notes": self.market_notes,
             "status": self.status.value if self.status else None,


Test patch:
diff --git a/tests/test_baseflumine.py b/tests/test_baseflumine.py
index 634677d..32c9c2a 100644
--- a/tests/test_baseflumine.py
+++ b/tests/test_baseflumine.py
@@ -225,14 +225,11 @@ class BaseFlumineTest(unittest.TestCase):
         mock_event.event = mock_market_book
         self.base_flumine._process_close_market(mock_event)
         mock_market.close_market.assert_called_with()
-        mock_market.blotter.process_closed_market.assert_called_with(mock_market_book)
+        mock_market.blotter.process_closed_market.assert_not_called()
         mock_strategy.process_closed_market.assert_called_with(
             mock_market, mock_market_book
         )
         mock_log_control.assert_called_with(mock_event)
-        self.assertEqual(
-            self.base_flumine.cleared_market_queue.get(), mock_market_book["id"]
-        )
 
     @mock.patch("flumine.baseflumine.BaseFlumine.info")
     def test__process_close_market_no_market(self, mock_info):
diff --git a/tests/test_trade.py b/tests/test_trade.py
index 28c2935..3999e6a 100644
--- a/tests/test_trade.py
+++ b/tests/test_trade.py
@@ -72,8 +72,10 @@ class TradeTest(unittest.TestCase):
         self.assertFalse(self.trade.complete)
 
     def test_trade_complete_offset(self):
-        self.trade.offset_orders = [1]
+        self.trade.offset_orders = [mock.Mock(complete=False)]
         self.assertFalse(self.trade.complete)
+        self.trade.offset_orders = [mock.Mock(complete=True)]
+        self.assertTrue(self.trade.complete)
 
     def test_trade_complete_replace_order(self):
         self.assertTrue(self.trade.complete)
@@ -87,7 +89,16 @@ class TradeTest(unittest.TestCase):
         mock_order_type.EXCHANGE = "SYM"
         mock_order = mock.Mock()
         mock_order.EXCHANGE = "SYM"
-        self.trade.create_order("BACK", mock_order_type, handicap=1, order=mock_order)
+        self.trade.create_order(
+            "BACK", mock_order_type, handicap=1, order=mock_order, context={1: 2}
+        )
+        mock_order.assert_called_with(
+            trade=self.trade,
+            side="BACK",
+            order_type=mock_order_type,
+            handicap=1,
+            context={1: 2},
+        )
         self.assertEqual(self.trade.orders, [mock_order()])
 
     def test_create_order_error(self):
@@ -151,6 +162,7 @@ class TradeTest(unittest.TestCase):
             {
                 "id": str(self.trade.id),
                 "orders": [],
+                "offset_orders": [],
                 "place_reset_seconds": 12,
                 "reset_seconds": 34,
                 "strategy": str(self.mock_strategy),