Repository: liampauling/flumine
Pull request #211 (2020-06-08 15:22:11)

Task/185 cleared order


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 20f0a4a..6dbbb2f 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,14 @@
 Release History
 ---------------
 
+1.6.7 (2020-06-08)
++++++++++++++++++++
+
+**Improvements**
+
+- #185 cleared orders meta implemented
+- Order.elapsed_seconds_executable added
+
 1.6.6 (2020-06-08)
 +++++++++++++++++++
 
diff --git a/flumine/__version__.py b/flumine/__version__.py
index 540209f..baaf149 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.6.6"
+__version__ = "1.6.7"
 __author__ = "Liam Pauling"
 __license__ = "MIT"
diff --git a/flumine/baseflumine.py b/flumine/baseflumine.py
index b59a3f5..6e37962 100644
--- a/flumine/baseflumine.py
+++ b/flumine/baseflumine.py
@@ -202,7 +202,7 @@ class BaseFlumine:
         if market is None:
             logger.warning(
                 "Market %s not present when closing" % market_id,
-                extra={"market_id": market_id},
+                extra={"market_id": market_id, **self.info},
             )
             return
         market.close_market()
@@ -219,10 +219,24 @@ class BaseFlumine:
         )
 
     def _process_cleared_orders(self, event):
-        # todo update blotter?
+        market_id = event.event.market_id
+        market = self.markets.markets.get(market_id)
+        if market is None:
+            logger.warning(
+                "Market %s not present when clearing" % market_id,
+                extra={"market_id": market_id, **self.info},
+            )
+            return
+
+        meta_orders = market.blotter.process_cleared_orders(event.event)
+        self.log_control(events.ClearedOrdersMetaEvent(meta_orders))
         logger.info(
             "Market closed and cleared",
-            extra={"market_id": event.event.market_id, **self.info},
+            extra={
+                "market_id": market_id,
+                "order_count": len(meta_orders),
+                **self.info,
+            },
         )
 
     def _process_cleared_markets(self, event: events.ClearedMarketsEvent):
diff --git a/flumine/markets/blotter.py b/flumine/markets/blotter.py
index adf2fd3..04f6814 100644
--- a/flumine/markets/blotter.py
+++ b/flumine/markets/blotter.py
@@ -2,7 +2,7 @@ import logging
 from typing import Iterable
 
 from ..utils import chunks, calculate_exposure
-from ..order.order import BaseOrder, OrderStatus
+from ..order.order import BaseOrder
 from ..order.orderpackage import OrderPackageType, BetfairOrderPackage
 
 logger = logging.getLogger(__name__)
@@ -85,6 +85,10 @@ class Blotter:
                 ):
                     order.runner_status = runner.status
 
+    def process_cleared_orders(self, cleared_orders) -> list:
+        # todo update order.cleared?
+        return [order for order in self]
+
     """ position """
 
     def selection_exposure(self, strategy, lookup: tuple) -> float:
diff --git a/flumine/order/order.py b/flumine/order/order.py
index 62efe83..7b69613 100644
--- a/flumine/order/order.py
+++ b/flumine/order/order.py
@@ -194,6 +194,13 @@ class BaseOrder:
         else:
             return
 
+    @property
+    def elapsed_seconds_executable(self) -> Optional[float]:
+        if self.date_time_execution_complete and self.responses.date_time_placed:
+            return (
+                self.date_time_execution_complete - self.responses.date_time_placed
+            ).total_seconds()
+
     # todo cached properties?
     @property
     def market_id(self) -> str:
diff --git a/flumine/order/responses.py b/flumine/order/responses.py
index df8a987..1717c0a 100644
--- a/flumine/order/responses.py
+++ b/flumine/order/responses.py
@@ -22,7 +22,6 @@ class Responses:
 
     def replaced(self, response):
         self.replace_responses.append(response)
-        # self.date_time_placed = datetime.datetime.utcnow()  # todo?
 
     def updated(self, response):
         self.update_responses.append(response)


Test patch:
diff --git a/tests/test_baseflumine.py b/tests/test_baseflumine.py
index f90b498..6fb780b 100644
--- a/tests/test_baseflumine.py
+++ b/tests/test_baseflumine.py
@@ -195,7 +195,8 @@ class BaseFlumineTest(unittest.TestCase):
             self.base_flumine.cleared_market_queue.get(), mock_market.market_id
         )
 
-    def test__process_close_market_no_market(self):
+    @mock.patch("flumine.baseflumine.BaseFlumine.info")
+    def test__process_close_market_no_market(self, mock_info):
         mock_market = mock.Mock()
         mock_market.market_book.streaming_unique_id = 2
         mock_markets = mock.Mock()
@@ -207,10 +208,33 @@ class BaseFlumineTest(unittest.TestCase):
         self.base_flumine._process_close_market(mock_event)
         mock_market.close_market.assert_not_called()
 
-    def test__process_cleared_orders(self):
+    @mock.patch("flumine.baseflumine.events")
+    @mock.patch("flumine.baseflumine.BaseFlumine.log_control")
+    @mock.patch("flumine.baseflumine.BaseFlumine.info")
+    def test__process_cleared_orders(self, mock_info, mock_log_control, mock_events):
+        mock_market = mock.Mock()
+        mock_market.blotter.process_cleared_orders.return_value = []
+        mock_markets = mock.Mock()
+        mock_markets.markets = {"1.23": mock_market}
+        self.base_flumine.markets = mock_markets
+        mock_event = mock.Mock()
+        mock_event.event.market_id = "1.23"
+        mock_event.event.orders = []
+        self.base_flumine._process_cleared_orders(mock_event)
+        mock_market.blotter.process_cleared_orders.assert_called_with(mock_event.event)
+        mock_log_control.assert_called_with(mock_events.ClearedOrdersMetaEvent())
+
+    @mock.patch("flumine.baseflumine.BaseFlumine.info")
+    def test__process_cleared_orders_no_market(self, mock_info):
+        mock_market = mock.Mock()
+        mock_markets = mock.Mock()
+        mock_markets.markets = {"1.23": mock_market}
+        self.base_flumine.markets = mock_markets
         mock_event = mock.Mock()
+        mock_event.event.market_id = "1.24"
         mock_event.event.orders = []
         self.base_flumine._process_cleared_orders(mock_event)
+        mock_market.blotter.process_cleared_orders.assert_not_called()
 
     def test__process_cleared_markets(self):
         mock_event = mock.Mock()
diff --git a/tests/test_blotter.py b/tests/test_blotter.py
index 1d7ec38..fed480f 100644
--- a/tests/test_blotter.py
+++ b/tests/test_blotter.py
@@ -1,7 +1,7 @@
 import unittest
 from unittest import mock
 
-from flumine.markets.blotter import Blotter, OrderStatus, OrderPackageType
+from flumine.markets.blotter import Blotter, OrderPackageType
 
 
 class BlotterTest(unittest.TestCase):
@@ -90,6 +90,10 @@ class BlotterTest(unittest.TestCase):
         self.blotter.process_closed_market(mock_market_book)
         self.assertEqual(mock_order.runner_status, mock_runner.status)
 
+    def test_process_cleared_orders(self):
+        mock_cleared_orders = mock.Mock()
+        self.assertEqual(self.blotter.process_cleared_orders(mock_cleared_orders), [])
+
     def test_selection_exposure(self):
         mock_strategy = mock.Mock()
         mock_trade = mock.Mock(strategy=mock_strategy)
diff --git a/tests/test_order.py b/tests/test_order.py
index d847806..ac2edd7 100644
--- a/tests/test_order.py
+++ b/tests/test_order.py
@@ -199,6 +199,14 @@ class BaseOrderTest(unittest.TestCase):
         self.order.responses = mock_responses
         self.assertGreaterEqual(self.order.elapsed_seconds, 0)
 
+    def test_elapsed_seconds_executable(self):
+        self.assertIsNone(self.order.elapsed_seconds_executable)
+        mock_responses = mock.Mock()
+        mock_responses.date_time_placed = datetime.datetime.utcnow()
+        self.order.responses = mock_responses
+        self.order.date_time_execution_complete = datetime.datetime.utcnow()
+        self.assertGreaterEqual(self.order.elapsed_seconds_executable, 0)
+
     def test_market_id(self):
         self.assertEqual(self.order.market_id, self.mock_trade.market_id)