Repository: liampauling/flumine
Pull request #497 (2021-09-14 07:39:02)

Release/1.19.17


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index a679acf..3071f22 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,13 @@
 Release History
 ---------------
 
+1.19.17 (2021-09-14)
++++++++++++++++++++
+
+**Improvements**
+
+- Backtest speed improvements
+
 1.19.16 (2021-09-13)
 +++++++++++++++++++
 
diff --git a/flumine/__version__.py b/flumine/__version__.py
index f57fcac..8da91c8 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.19.16"
+__version__ = "1.19.17"
 __author__ = "Liam Pauling"
 __license__ = "MIT"
diff --git a/flumine/order/order.py b/flumine/order/order.py
index 1e9b8ba..6e51908 100644
--- a/flumine/order/order.py
+++ b/flumine/order/order.py
@@ -57,12 +57,14 @@ class BaseOrder:
         self.trade = trade
         self.side = side
         self.order_type = order_type
+        self.selection_id = trade.selection_id
         self.handicap = handicap
         self.lookup = self.market_id, self.selection_id, self.handicap
 
         self.runner_status = None  # RunnerBook.status
         self.number_of_dead_heat_winners = None
         self.status = None
+        self.complete = False
         self.status_log = []
         self.violation_msg = None
         self.context = context or {}  # store order specific notes/triggers
@@ -90,8 +92,9 @@ class BaseOrder:
     def _update_status(self, status: OrderStatus) -> None:
         self.status_log.append(status)
         self.status = status
+        self.complete = self._is_complete()
         logger.info("Order status update: %s" % self.status.value, extra=self.info)
-        if self.trade.complete and status != OrderStatus.VIOLATION:
+        if self.complete and self.trade.complete and status != OrderStatus.VIOLATION:
             self.trade.complete_trade()
 
     def placing(self) -> None:
@@ -150,17 +153,7 @@ class BaseOrder:
     def update_current_order(self, current_order: CurrentOrder) -> None:
         self.responses.current_order = current_order
 
-    @property
-    def current_order(self) -> Union[CurrentOrder, Simulated]:
-        if self._simulated:
-            return self.simulated
-        elif self.responses.current_order:
-            return self.responses.current_order
-        elif self.responses.place_response:
-            return self.responses.place_response
-
-    @property
-    def complete(self) -> bool:
+    def _is_complete(self) -> bool:
         """Returns False if order is
         live or pending in the market"""
         if self.status in [
@@ -180,6 +173,15 @@ class BaseOrder:
         else:
             return False  # default to False
 
+    @property
+    def current_order(self) -> Union[CurrentOrder, Simulated]:
+        if self._simulated:
+            return self.simulated
+        elif self.responses.current_order:
+            return self.responses.current_order
+        elif self.responses.place_response:
+            return self.responses.place_response
+
     @property
     def average_price_matched(self) -> float:
         raise NotImplementedError
@@ -243,10 +245,6 @@ class BaseOrder:
     def market_id(self) -> str:
         return self.trade.market_id
 
-    @property
-    def selection_id(self) -> int:
-        return self.trade.selection_id
-
     @property
     def customer_order_ref(self) -> str:
         return "{0}{1}{2}".format(self.trade.strategy.name_hash, self.sep, self.id)


Test patch:
diff --git a/flumine/backtest/backtest.py b/flumine/backtest/backtest.py
index 713aa19..acf7fb4 100644
--- a/flumine/backtest/backtest.py
+++ b/flumine/backtest/backtest.py
@@ -157,18 +157,20 @@ class FlumineBacktest(BaseFlumine):
         """
         blotter = market.blotter
         for order in blotter.live_orders:
-            if not order.complete:
+            if order.complete:
+                blotter.complete_order(order)
+            else:
                 if order.order_type.ORDER_TYPE == OrderTypes.LIMIT:
                     if order.size_remaining == 0:
                         order.execution_complete()
+                        blotter.complete_order(order)
                 elif order.order_type.ORDER_TYPE in [
                     OrderTypes.LIMIT_ON_CLOSE,
                     OrderTypes.MARKET_ON_CLOSE,
                 ]:
                     if order.current_order.status == "EXECUTION_COMPLETE":
                         order.execution_complete()
-            if order.complete:
-                blotter.complete_order(order)
+                        blotter.complete_order(order)
         for strategy in self.strategies:
             strategy_orders = blotter.strategy_orders(strategy)
             utils.call_process_orders_error_handling(strategy, market, strategy_orders)
diff --git a/flumine/backtest/simulated.py b/flumine/backtest/simulated.py
index e357617..0ebdb32 100644
--- a/flumine/backtest/simulated.py
+++ b/flumine/backtest/simulated.py
@@ -43,9 +43,7 @@ class Simulated:
             runner = self._get_runner(market_book)
             self._process_sp(market_book.publish_time_epoch, runner)
 
-        elif (
-            self.order.order_type.ORDER_TYPE == OrderTypes.LIMIT and self.size_remaining
-        ):
+        elif self.order.order_type.ORDER_TYPE == OrderTypes.LIMIT:
             if market_book.version != self.market_version:
                 self.market_version = market_book.version  # update for next time
                 if market_book.status == "SUSPENDED":  # Material change
diff --git a/tests/test_order.py b/tests/test_order.py
index de1fd4b..7823dee 100644
--- a/tests/test_order.py
+++ b/tests/test_order.py
@@ -31,6 +31,7 @@ class BaseOrderTest(unittest.TestCase):
         self.assertEqual(self.order.trade, self.mock_trade)
         self.assertEqual(self.order.side, "BACK")
         self.assertEqual(self.order.order_type, self.mock_order_type)
+        self.assertEqual(self.order.selection_id, self.mock_trade.selection_id)
         self.assertEqual(self.order.handicap, 1)
         self.assertEqual(
             self.order.lookup,
@@ -38,6 +39,7 @@ class BaseOrderTest(unittest.TestCase):
         )
         self.assertIsNone(self.order.runner_status)
         self.assertIsNone(self.order.status)
+        self.assertFalse(self.order.complete)
         self.assertEqual(self.order.status_log, [])
         self.assertIsNone(self.order.violation_msg)
         self.assertEqual(self.order.context, {1: 2})
@@ -54,13 +56,15 @@ class BaseOrderTest(unittest.TestCase):
         self.assertFalse(self.order.simulated)
         self.assertFalse(self.order._simulated)
 
+    @mock.patch("flumine.order.order.BaseOrder._is_complete")
     @mock.patch("flumine.order.order.BaseOrder.info")
-    def test__update_status(self, mock_info):
+    def test__update_status(self, mock_info, mock__is_complete):
         self.mock_trade.complete = True
         self.order._update_status(OrderStatus.EXECUTION_COMPLETE)
         self.assertEqual(self.order.status_log, [OrderStatus.EXECUTION_COMPLETE])
         self.assertEqual(self.order.status, OrderStatus.EXECUTION_COMPLETE)
         self.mock_trade.complete_trade.assert_called()
+        mock__is_complete.assert_called()
 
     @mock.patch("flumine.order.order.BaseOrder._update_status")
     def test_placing(self, mock__update_status):
@@ -158,9 +162,9 @@ class BaseOrderTest(unittest.TestCase):
         self.assertTrue(order.simulated)
         self.assertTrue(order._simulated)
 
-    def test_complete(self):
+    def test__is_complete(self):
         self.order.status = None
-        self.assertFalse(self.order.complete)
+        self.assertFalse(self.order._is_complete())
         for s in [
             OrderStatus.PENDING,
             OrderStatus.CANCELLING,
@@ -169,14 +173,14 @@ class BaseOrderTest(unittest.TestCase):
             OrderStatus.EXECUTABLE,
         ]:
             self.order.status = s
-            self.assertFalse(self.order.complete)
+            self.assertFalse(self.order._is_complete())
         for s in [
             OrderStatus.EXECUTION_COMPLETE,
             OrderStatus.EXPIRED,
             OrderStatus.VIOLATION,
         ]:
             self.order.status = s
-            self.assertTrue(self.order.complete)
+            self.assertTrue(self.order._is_complete())
 
     def test_average_price_matched(self):
         with self.assertRaises(NotImplementedError):
@@ -223,9 +227,6 @@ class BaseOrderTest(unittest.TestCase):
     def test_market_id(self):
         self.assertEqual(self.order.market_id, self.mock_trade.market_id)
 
-    def test_selection_id(self):
-        self.assertEqual(self.order.selection_id, self.mock_trade.selection_id)
-
     def test_lookup(self):
         self.assertEqual(
             self.order.lookup,