Repository: liampauling/flumine
Pull request #430 (2021-05-14 07:58:35)

refactor snap to send MarketBook when not open regardless of filter
instances where MoC bets where not processed correctly when inplay set to False on the listenerKwargs


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 6ffa5f6..3d9b43d 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,22 +3,6 @@
 Release History
 ---------------
 
-1.18.8 (2021-05-14)
-+++++++++++++++++++
-
-**Improvements**
-
-- Simulation optimisations
-
-**Bug Fixes**
-
-- #173 dead heat profit calculation (@lunswor)
-- listenerKwargs inplay / MoC / SP orders fix (@jsphon)
-
-**Libraries**
-
-- black upgraded to 21.5b1
-
 1.18.7 (2021-05-10)
 +++++++++++++++++++
 
diff --git a/flumine/__version__.py b/flumine/__version__.py
index 2fa773b..21cea79 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.18.8"
+__version__ = "1.18.7"
 __author__ = "Liam Pauling"
 __license__ = "MIT"
diff --git a/flumine/events/events.py b/flumine/events/events.py
index 69a37c6..d5ad22a 100644
--- a/flumine/events/events.py
+++ b/flumine/events/events.py
@@ -29,12 +29,10 @@ class QueueType(Enum):
     LOGGING = "Logging queue"
 
 
-class BaseEvent:
+class BaseEvent:  # todo __slots__?
     EVENT_TYPE = None
     QUEUE_TYPE = None
 
-    __slots__ = ["_time_created", "event", "callback"]
-
     def __init__(self, event):
         self._time_created = datetime.datetime.utcnow()
         self.event = event
diff --git a/flumine/markets/middleware.py b/flumine/markets/middleware.py
index 4a14ad1..c8eea79 100644
--- a/flumine/markets/middleware.py
+++ b/flumine/markets/middleware.py
@@ -3,7 +3,7 @@ from collections import defaultdict
 from betfairlightweight.resources.bettingresources import RunnerBook
 
 from ..order.order import OrderStatus, OrderTypes
-from ..utils import wap
+from ..utils import get_price, wap
 
 logger = logging.getLogger(__name__)
 
@@ -134,11 +134,11 @@ class SimulatedMiddleware(Middleware):
 
     @staticmethod
     def _process_simulated_orders(market, market_analytics: dict) -> None:
-        for order in market.blotter._live_orders:
+        for order in market.blotter.live_orders:
             if order.simulated and order.status == OrderStatus.EXECUTABLE:
-                runner_analytics = market_analytics[
+                runner_analytics = market_analytics.get(
                     (order.selection_id, order.handicap)
-                ]
+                )
                 order.simulated(market.market_book, runner_analytics)
 
     @staticmethod
@@ -169,45 +169,36 @@ class RunnerAnalytics:
         if update:
             self.middle = self._calculate_middle(self._runner)  # use last update
             self.matched = self._calculate_matched(runner)
-            _tv = runner.ex.traded_volume
-            if self._traded_volume == _tv:
-                self.traded = {}
-            else:
-                self.traded = self._calculate_traded(_tv)
-            self._traded_volume = _tv
+            self.traded = self._calculate_traded(runner.ex.traded_volume)
+            self._traded_volume = runner.ex.traded_volume
             self._runner = runner
         else:
             self.matched = 0
             self.traded = {}
 
     def _calculate_traded(self, traded_volume: list) -> dict:
-        p_v, traded = self._p_v, {}
-        # create dictionary
-        c_v = {i["price"]: i["size"] for i in traded_volume}
-        # calculate difference
-        for key, value in c_v.items():
-            if key in p_v:
-                new_value = float(value) - float(p_v[key])
-                if new_value > 0:
-                    traded[key] = round(new_value, 2)
-            else:
-                traded[key] = value
-        # cache for next update
-        self._p_v = c_v
-        return traded
+        if self._traded_volume == traded_volume:
+            return {}
+        else:
+            p_v, traded = self._p_v, {}
+            # create dictionary
+            c_v = {i["price"]: i["size"] for i in traded_volume}
+            # calculate difference
+            for key, value in c_v.items():
+                if key in p_v:
+                    new_value = float(value) - float(p_v[key])
+                    if new_value > 0:
+                        traded[key] = round(new_value, 2)
+                else:
+                    traded[key] = value
+            # cache for next update
+            self._p_v = c_v
+            return traded
 
     @staticmethod
     def _calculate_middle(runner: RunnerBook) -> float:
-        _back = runner.ex.available_to_back
-        if _back:
-            back = _back[0]["price"]
-        else:
-            back = 0
-        _lay = runner.ex.available_to_lay
-        if _lay:
-            lay = _lay[0]["price"]
-        else:
-            lay = 1001
+        back = get_price(runner.ex.available_to_back, 0) or 0
+        lay = get_price(runner.ex.available_to_lay, 0) or 1001
         return (float(back) + float(lay)) / 2
 
     def _calculate_matched(self, runner: RunnerBook) -> float:
@@ -215,7 +206,4 @@ class RunnerAnalytics:
         total_matched = (
             runner.total_matched or prev_total_matched
         )  # handles non-runner -> 0
-        if total_matched != prev_total_matched:
-            return round(total_matched - prev_total_matched, 2)
-        else:
-            return 0.0
+        return round(total_matched - prev_total_matched, 2)
diff --git a/flumine/streams/historicalstream.py b/flumine/streams/historicalstream.py
index 0cff5ba..04fdaed 100644
--- a/flumine/streams/historicalstream.py
+++ b/flumine/streams/historicalstream.py
@@ -56,8 +56,8 @@ class FlumineMarketStream(MarketStream):
         for cache in list(self._caches.values()):
             if market_ids and cache.market_id not in market_ids:
                 continue
-            # if market has closed send regardless
-            if cache._definition_status != "CLOSED":
+            # if market is not open (closed/suspended) send regardless
+            if cache._definition_status == "OPEN":
                 if self._listener.inplay:
                     if not cache._definition_in_play:
                         continue


Test patch:
diff --git a/requirements-test.txt b/requirements-test.txt
index ef0c0d2..7e1a600 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -1,5 +1,5 @@
 # Tests & Linting
-black==21.5b1
+black==21.5b0
 coverage
 
 # Documentation
diff --git a/tests/test_events.py b/tests/test_events.py
index 86efbd4..2e1cf39 100644
--- a/tests/test_events.py
+++ b/tests/test_events.py
@@ -21,5 +21,6 @@ class BaseEventTest(unittest.TestCase):
         self.assertGreaterEqual(self.base_event.elapsed_seconds, 0)
 
     def test_str(self):
-        self.base_event = events.MarketBookEvent(None)
+        self.base_event.EVENT_TYPE = events.EventType.MARKET_BOOK
+        self.base_event.QUEUE_TYPE = events.QueueType.HANDLER
         self.assertEqual(str(self.base_event), "<MARKET_BOOK [HANDLER]>")
diff --git a/tests/test_middleware.py b/tests/test_middleware.py
index 73806e3..2ae65b2 100644
--- a/tests/test_middleware.py
+++ b/tests/test_middleware.py
@@ -170,17 +170,15 @@ class SimulatedMiddlewareTest(unittest.TestCase):
 
     def test__process_simulated_orders(self):
         mock_market_book = mock.Mock()
+        mock_order = mock.Mock()
+        mock_order.status = OrderStatus.EXECUTABLE
         mock_market = mock.Mock()
-        mock_order = mock.Mock(
-            selection_id=123, handicap=1, status=OrderStatus.EXECUTABLE
-        )
-        mock_order_two = mock.Mock(
-            selection_id=123, handicap=1, status=OrderStatus.PENDING
-        )
-        mock_order_three = mock.Mock(
-            selection_id=123, handicap=1, status=OrderStatus.EXECUTABLE, simulated=False
-        )
-        mock_market.blotter._live_orders = [
+        mock_order_two = mock.Mock()
+        mock_order_two.status = OrderStatus.PENDING
+        mock_order_three = mock.Mock()
+        mock_order_three.status = OrderStatus.EXECUTABLE
+        mock_order_three._simulated = False
+        mock_market.blotter.live_orders = [
             mock_order,
             mock_order_two,
             mock_order_three,
diff --git a/tests/test_streams.py b/tests/test_streams.py
index aff68d8..b84e3af 100644
--- a/tests/test_streams.py
+++ b/tests/test_streams.py
@@ -609,10 +609,10 @@ class TestFlumineMarketStream(unittest.TestCase):
             "1.123": mock.Mock(
                 publish_time=123,
                 market_definition={
-                    "status": "OPEN",
-                    "inPlay": False,
                     "marketTime": 456,
                 },
+                _definition_status="OPEN",
+                _definition_in_play=False,
             )
         }
         self.assertEqual(len(self.stream.snap()), 1)
@@ -620,10 +620,10 @@ class TestFlumineMarketStream(unittest.TestCase):
             "1.123": mock.Mock(
                 publish_time=123,
                 market_definition={
-                    "status": "OPEN",
-                    "inPlay": False,
                     "marketTime": 1234567,
                 },
+                _definition_status="OPEN",
+                _definition_in_play=False,
             )
         }
         self.assertEqual(len(self.stream.snap()), 0)