Repository: liampauling/flumine
Pull request #378 (2021-02-25 08:11:09)

Allow patching of stream retry wait arg


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 2cdc7d3..d8c91cb 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,17 @@
 Release History
 ---------------
 
+1.17.2 (2021-02-25)
++++++++++++++++++++
+
+**Improvements**
+
+- Allow patching of stream retry wait arg
+
+**Bug Fixes**
+
+- Incorrect handling of potential exposure in control
+
 1.17.1 (2021-02-24)
 +++++++++++++++++++
 
diff --git a/flumine/__version__.py b/flumine/__version__.py
index eee2122..5e572ec 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.1"
+__version__ = "1.17.2"
 __author__ = "Liam Pauling"
 __license__ = "MIT"
diff --git a/flumine/controls/tradingcontrols.py b/flumine/controls/tradingcontrols.py
index 00abb9c..dcbefe9 100644
--- a/flumine/controls/tradingcontrols.py
+++ b/flumine/controls/tradingcontrols.py
@@ -140,12 +140,15 @@ class StrategyExposure(BaseControl):
 
             # per selection
             market = self.flumine.markets.markets[order.market_id]
-            exposure = market.blotter.selection_exposure(strategy, lookup=order.lookup)
-            if exposure > strategy.max_selection_exposure:
+            selection_exposure = market.blotter.selection_exposure(
+                strategy, lookup=order.lookup
+            )
+            potential_exposure = selection_exposure + exposure
+            if potential_exposure > strategy.max_selection_exposure:
                 return self._on_error(
                     order,
-                    "Potential selection exposure ({0}) is greater than strategy.max_selection_exposure ({1})".format(
-                        exposure,
+                    "Potential selection exposure ({0:.2f}) is greater than strategy.max_selection_exposure ({1})".format(
+                        potential_exposure,
                         strategy.max_selection_exposure,
                     ),
                 )
diff --git a/flumine/streams/basestream.py b/flumine/streams/basestream.py
index b193de1..7d0b539 100644
--- a/flumine/streams/basestream.py
+++ b/flumine/streams/basestream.py
@@ -3,6 +3,7 @@ import queue
 import logging
 import betfairlightweight
 from betfairlightweight import StreamListener
+from tenacity import wait_exponential
 
 logger = logging.getLogger(__name__)
 
@@ -11,6 +12,7 @@ class BaseStream(threading.Thread):
 
     LISTENER = StreamListener
     MAX_LATENCY = 0.5
+    RETRY_WAIT = wait_exponential(multiplier=1, min=2, max=20)
 
     def __init__(
         self,
diff --git a/flumine/streams/datastream.py b/flumine/streams/datastream.py
index 4781d83..9651ec6 100644
--- a/flumine/streams/datastream.py
+++ b/flumine/streams/datastream.py
@@ -1,5 +1,5 @@
 import logging
-from tenacity import retry, wait_exponential
+from tenacity import retry
 from betfairlightweight import StreamListener
 from betfairlightweight import BetfairError
 from betfairlightweight.streaming.stream import BaseStream as BFBaseStream
@@ -10,6 +10,8 @@ from ..exceptions import ListenerError
 
 logger = logging.getLogger(__name__)
 
+RETRY_WAIT = BaseStream.RETRY_WAIT
+
 
 """
 Custom listener that doesn't do any processing,
@@ -98,7 +100,7 @@ class DataStream(BaseStream):
         BaseStream.__init__(self, *args, **kwargs)
         self._listener = self.LISTENER(output_queue=self.flumine.handler_queue)
 
-    @retry(wait=wait_exponential(multiplier=1, min=2, max=20))
+    @retry(wait=RETRY_WAIT)
     def run(self) -> None:
         logger.info(
             "Starting DataStream {0}".format(self.stream_id),
diff --git a/flumine/streams/marketstream.py b/flumine/streams/marketstream.py
index bc3af27..82834e6 100644
--- a/flumine/streams/marketstream.py
+++ b/flumine/streams/marketstream.py
@@ -1,16 +1,18 @@
 import queue
 import logging
 from betfairlightweight import BetfairError
-from tenacity import retry, wait_exponential
+from tenacity import retry
 
 from .basestream import BaseStream
 from ..events.events import MarketBookEvent
 
 logger = logging.getLogger(__name__)
 
+RETRY_WAIT = BaseStream.RETRY_WAIT
+
 
 class MarketStream(BaseStream):
-    @retry(wait=wait_exponential(multiplier=1, min=2, max=20))
+    @retry(wait=RETRY_WAIT)
     def run(self) -> None:
         logger.info(
             "Starting MarketStream {0}".format(self.stream_id),
diff --git a/flumine/streams/orderstream.py b/flumine/streams/orderstream.py
index d7e8f56..d816c17 100644
--- a/flumine/streams/orderstream.py
+++ b/flumine/streams/orderstream.py
@@ -1,7 +1,7 @@
 import queue
 import logging
 from betfairlightweight import BetfairError, filters
-from tenacity import retry, wait_exponential
+from tenacity import retry
 
 from .basestream import BaseStream
 from ..events.events import CurrentOrdersEvent
@@ -9,9 +9,11 @@ from .. import config
 
 logger = logging.getLogger(__name__)
 
+RETRY_WAIT = BaseStream.RETRY_WAIT
+
 
 class OrderStream(BaseStream):
-    @retry(wait=wait_exponential(multiplier=1, min=2, max=20))
+    @retry(wait=RETRY_WAIT)
     def run(self) -> None:
         logger.info(
             "Starting OrderStream {0}".format(self.stream_id),


Test patch:
diff --git a/tests/test_tradingcontrols.py b/tests/test_tradingcontrols.py
index 9d82705..8ddb182 100644
--- a/tests/test_tradingcontrols.py
+++ b/tests/test_tradingcontrols.py
@@ -381,14 +381,13 @@ class TestStrategyExposure(unittest.TestCase):
         order2.average_price_matched = 0.0
         order2.size_matched = 0
 
-        self.market.blotter["order1"] = order1
         self.market.blotter["order2"] = order2
 
         self.trading_control._validate(order1, OrderPackageType.PLACE)
         self.assertEqual(1, mock_on_error.call_count)
         mock_on_error.assert_called_with(
             order1,
-            "Potential selection exposure (14.0) is greater than strategy.max_selection_exposure (10)",
+            "Potential selection exposure (14.00) is greater than strategy.max_selection_exposure (10)",
         )
 
     @mock.patch("flumine.controls.tradingcontrols.StrategyExposure._on_error")
@@ -428,7 +427,7 @@ class TestStrategyExposure(unittest.TestCase):
         mock_market = mock.Mock()
         mock_market.blotter.selection_exposure.return_value = 12.0
         self.mock_flumine.markets.markets = {"1.234": mock_market}
-        mock_order = mock.Mock(market_id="1.234", lookup=(1, 2, 3))
+        mock_order = mock.Mock(market_id="1.234", lookup=(1, 2, 3), side="LAY")
         mock_order.trade.strategy.max_order_exposure = 10
         mock_order.trade.strategy.max_selection_exposure = 10
         mock_order.order_type.ORDER_TYPE = OrderTypes.LIMIT
@@ -438,5 +437,5 @@ class TestStrategyExposure(unittest.TestCase):
         self.trading_control._validate(mock_order, OrderPackageType.PLACE)
         mock_on_error.assert_called_with(
             mock_order,
-            "Potential selection exposure (12.0) is greater than strategy.max_selection_exposure (10)",
+            "Potential selection exposure (12.12) is greater than strategy.max_selection_exposure (10)",
         )