Repository: liampauling/flumine
Pull request #207 (2020-06-08 09:30:21)

log order moved to after execution (require betId)
baseexecution cleaned up
trade created/complete dates added
balance call interval updated


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index d714411..b767a9d 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -3,6 +3,20 @@
 Release History
 ---------------
 
+1.6.5 (2020-06-08)
++++++++++++++++++++
+
+**Improvements**
+
+- #205 MarketBook publishTime added to simulated.matched / order.execution_complete time added
+- Controls error message added
+- Info properties improved
+- Order/Trade .complete refactored
+
+**Bug Fixes**
+
+- Log order moved to after execution (missing betId)
+
 1.6.4 (2020-06-08)
 +++++++++++++++++++
 
diff --git a/flumine/__version__.py b/flumine/__version__.py
index facafdb..1310719 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.4"
+__version__ = "1.6.5"
 __author__ = "Liam Pauling"
 __license__ = "MIT"
diff --git a/flumine/controls/__init__.py b/flumine/controls/__init__.py
index ecb8ff0..ba13cd9 100644
--- a/flumine/controls/__init__.py
+++ b/flumine/controls/__init__.py
@@ -1,6 +1,5 @@
 import logging
 
-# from ..order.process import strategy_reset
 from ..order.orderpackage import BaseOrderPackage
 from ..order.order import BaseOrder
 
@@ -20,11 +19,9 @@ class BaseControl:
     def _validate(self, order_package: BaseOrderPackage) -> None:
         raise NotImplementedError
 
-    def _on_error(self, order: BaseOrder) -> None:
+    def _on_error(self, order: BaseOrder, error: str) -> None:
         order.violation()
         logger.warning(
             "Order has violated {0} and will not be placed".format(self.NAME),
-            extra=order.info,
+            extra={"control": self.NAME, "error": error, "order": order.info},
         )
-        # if order.flumine_order_type == "initial":
-        #     strategy_reset(order)
diff --git a/flumine/controls/clientcontrols.py b/flumine/controls/clientcontrols.py
index 6d06da4..24779a8 100644
--- a/flumine/controls/clientcontrols.py
+++ b/flumine/controls/clientcontrols.py
@@ -41,7 +41,12 @@ class MaxOrderCount(BaseControl):
             self.place_requests += len(order_package)
             if not self.safe:  # and order.flumine_order_type == "initial"
                 for order in order_package:
-                    self._on_error(order)
+                    self._on_error(
+                        order,
+                        "Max Order Count has been reached ({0}) for current hour".format(
+                            self.transaction_count
+                        ),
+                    )
         elif order_package.package_type == OrderPackageType.CANCEL:
             self.cancel_requests += len(order_package)
         elif order_package.package_type == OrderPackageType.UPDATE:
diff --git a/flumine/controls/tradingcontrols.py b/flumine/controls/tradingcontrols.py
index 8e8f620..e714c0b 100644
--- a/flumine/controls/tradingcontrols.py
+++ b/flumine/controls/tradingcontrols.py
@@ -36,27 +36,27 @@ class OrderValidation(BaseControl):
             self._validate_betfair_liability(order)
             self._validate_betfair_min_size(order, OrderTypes.MARKET_ON_CLOSE)
         else:
-            self._on_error(order)  # unknown orderType
+            self._on_error(order, "Unknown orderType")
 
     def _validate_betfair_size(self, order):
         if order.order_type.size is None:
-            self._on_error(order)
+            self._on_error(order, "Order size is None")
         elif order.order_type.size <= 0:
-            self._on_error(order)
+            self._on_error(order, "Order size is less than 0")
         elif order.order_type.size != round(order.order_type.size, 2):
-            self._on_error(order)
+            self._on_error(order, "Order size has more than 2dp")
 
     def _validate_betfair_price(self, order):
         if order.order_type.price is None:
-            self._on_error(order)
+            self._on_error(order, "Order price is None")
         elif utils.as_dec(order.order_type.price) not in utils.PRICES:
-            self._on_error(order)
+            self._on_error(order, "Order price is not valid")
 
     def _validate_betfair_liability(self, order):
         if order.order_type.liability is None:
-            self._on_error(order)
+            self._on_error(order, "Order liability is None")
         elif order.order_type.liability <= 0:
-            self._on_error(order)
+            self._on_error(order, "Order liability is less than 0")
 
     def _validate_betfair_min_size(self, order, order_type):
         client = self.flumine.client
@@ -66,18 +66,33 @@ class OrderValidation(BaseControl):
                 and (order.order_type.price * order.order_type.size)
                 < client.min_bet_payout
             ):
-                self._on_error(order)
+                self._on_error(
+                    order,
+                    "Order size is less than min bet size ({0}) or payout ({1}) for currency".format(
+                        client.min_bet_size, client.min_bet_payout
+                    ),
+                )
         else:  # todo is this correct?
             if (
                 order.side == "BACK"
                 and order.order_type.liability < client.min_bet_size
             ):
-                self._on_error(order)
+                self._on_error(
+                    order,
+                    "Liability is less than min bet size ({0}) for currency".format(
+                        client.min_bet_size
+                    ),
+                )
             elif (
                 order.side == "LAY"
                 and order.order_type.liability < client.min_bsp_liability
             ):
-                self._on_error(order)
+                self._on_error(
+                    order,
+                    "Liability is less than min BSP payout ({0}) for currency".format(
+                        client.min_bsp_liability
+                    ),
+                )
 
 
 class StrategyExposure(BaseControl):
@@ -111,7 +126,12 @@ class StrategyExposure(BaseControl):
 
                 # per order
                 if exposure > strategy.max_order_exposure:
-                    self._on_error(order)
+                    self._on_error(
+                        order,
+                        "Order exposure ({0}) is greater than strategy.max_order_strategy ({1})".format(
+                            exposure, strategy.max_order_exposure
+                        ),
+                    )
                     continue
 
                 # per selection
@@ -122,4 +142,10 @@ class StrategyExposure(BaseControl):
                 if (
                     current_selection_exposure - exposure
                 ) < -strategy.max_selection_exposure:
-                    self._on_error(order)
+                    self._on_error(
+                        order,
+                        "Potential selection exposure ({0}) is greater than strategy.max_selection_exposure ({1})".format(
+                            (current_selection_exposure - exposure),
+                            strategy.max_selection_exposure,
+                        ),
+                    )
diff --git a/flumine/execution/baseexecution.py b/flumine/execution/baseexecution.py
index a5787a5..07d9cc5 100644
--- a/flumine/execution/baseexecution.py
+++ b/flumine/execution/baseexecution.py
@@ -4,7 +4,6 @@ from concurrent.futures import ThreadPoolExecutor
 
 from ..order.orderpackage import BaseOrderPackage, OrderPackageType, BaseOrder
 
-
 logger = logging.getLogger(__name__)
 
 MAX_WORKERS = 32
@@ -73,6 +72,7 @@ class BaseExecution:
         if package_type == OrderPackageType.PLACE:
             order.responses.placed(instruction_report)
             order.bet_id = instruction_report.bet_id
+            self.flumine.log_control(order)
         elif package_type == OrderPackageType.CANCEL:
             order.responses.cancelled(instruction_report)
         elif package_type == OrderPackageType.UPDATE:
@@ -80,16 +80,12 @@ class BaseExecution:
         elif package_type == OrderPackageType.REPLACE:
             order.responses.placed(instruction_report)
             order.bet_id = instruction_report.bet_id
-        # self.flumine.log_control(order)  # todo log order
+            self.flumine.log_control(order)
 
     @property
     def handler_queue(self):
         return self.flumine.handler_queue
 
-    @property
-    def markets(self):
-        return self.flumine.markets
-
     def shutdown(self):
         logger.info("Shutting down Execution (%s)" % self.__class__.__name__)
         self._thread_pool.shutdown(wait=True)
diff --git a/flumine/flumine.py b/flumine/flumine.py
index 9902be5..6b9dced 100644
--- a/flumine/flumine.py
+++ b/flumine/flumine.py
@@ -60,7 +60,7 @@ class Flumine(BaseFlumine):
             worker.BackgroundWorker(
                 self,
                 function=worker.poll_account_balance,
-                interval=60,
+                interval=120,
                 start_delay=10,  # wait for login
             )
         )
diff --git a/flumine/markets/blotter.py b/flumine/markets/blotter.py
index 448cf8b..adf2fd3 100644
--- a/flumine/markets/blotter.py
+++ b/flumine/markets/blotter.py
@@ -72,10 +72,7 @@ class Blotter:
     @property
     def live_orders(self) -> bool:
         for order in self._orders.values():
-            if (
-                order.status == OrderStatus.EXECUTABLE
-                or order.trade.trade_complete is False
-            ):
+            if order.complete is False or order.trade.complete is False:
                 return True
         return False
 
diff --git a/flumine/markets/market.py b/flumine/markets/market.py
index a99c92f..a5d9b6d 100644
--- a/flumine/markets/market.py
+++ b/flumine/markets/market.py
@@ -39,7 +39,6 @@ class Market:
         if order.id not in self.blotter:
             self.blotter[order.id] = order
             self.flumine.log_control(events.TradeEvent(order.trade))  # todo dupes?
-            self.flumine.log_control(events.OrderEvent(order))
         else:
             return  # retry attempt so ignore?
         if execute:  # handles replaceOrder
diff --git a/flumine/order/order.py b/flumine/order/order.py
index d94da83..62efe83 100644
--- a/flumine/order/order.py
+++ b/flumine/order/order.py
@@ -57,6 +57,9 @@ class BaseOrder:
         self.simulated = Simulated(self)  # used in simulated execution
         self.publish_time = None  # marketBook.publish_time
 
+        self.date_time_created = datetime.datetime.utcnow()
+        self.date_time_execution_complete = None
+
     # status
     def _update_status(self, status: OrderStatus) -> None:
         self.status_log.append(status)
@@ -72,6 +75,7 @@ class BaseOrder:
 
     def execution_complete(self) -> None:
         self._update_status(OrderStatus.EXECUTION_COMPLETE)
+        self.date_time_execution_complete = datetime.datetime.utcnow()
         self.update_data.clear()
 
     def cancelling(self) -> None:
@@ -134,6 +138,29 @@ class BaseOrder:
         elif self.responses.place_response:
             return self.responses.place_response
 
+    @property
+    def complete(self) -> bool:
+        """ Returns False if order is
+        live or pending in the market"""
+        if self.status in [
+            OrderStatus.PENDING,
+            OrderStatus.CANCELLING,
+            OrderStatus.UPDATING,
+            OrderStatus.REPLACING,
+            OrderStatus.EXECUTABLE,
+        ]:
+            return False
+        elif self.status in [
+            OrderStatus.EXECUTION_COMPLETE,
+            OrderStatus.EXPIRED,
+            OrderStatus.VOIDED,
+            OrderStatus.LAPSED,
+            OrderStatus.VIOLATION,
+        ]:
+            return True
+        else:
+            return False  # default to False
+
     @property
     def average_price_matched(self) -> float:
         raise NotImplementedError
@@ -194,6 +221,16 @@ class BaseOrder:
             "customer_order_ref": self.customer_order_ref,
             "bet_id": self.bet_id,
             "trade": self.trade.info,
+            "order_type": self.order_type.info,
+            "info": {
+                "side": self.side,
+                "size_matched": self.size_matched,
+                "size_remaining": self.size_remaining,
+                "size_cancelled": self.size_cancelled,
+                "size_lapsed": self.size_lapsed,
+                "size_voided": self.size_voided,
+                "average_price_matched": self.average_price_matched,
+            },
             "status": self.status.value if self.status else None,
             "status_log": ", ".join([s.value for s in self.status_log]),
         }
diff --git a/flumine/order/ordertype.py b/flumine/order/ordertype.py
index 7f1f945..9ec9b59 100644
--- a/flumine/order/ordertype.py
+++ b/flumine/order/ordertype.py
@@ -22,6 +22,10 @@ class BaseOrderType:
     def place_instruction(self) -> dict:
         raise NotImplementedError
 
+    @property
+    def info(self):
+        raise NotImplementedError
+
 
 class LimitOrder(BaseOrderType):
 
@@ -57,6 +61,19 @@ class LimitOrder(BaseOrderType):
             bet_target_size=self.bet_target_size,
         )
 
+    @property
+    def info(self):
+        return {
+            "order_type": self.ORDER_TYPE.value,
+            "price": self.price,
+            "size": self.size,
+            "persistence_type": self.persistence_type,
+            "time_in_force": self.time_in_force,
+            "min_fill_size": self.min_fill_size,
+            "bet_target_type": self.bet_target_type,
+            "bet_target_size": self.bet_target_size,
+        }
+
 
 class LimitOnCloseOrder(BaseOrderType):
 
@@ -70,6 +87,14 @@ class LimitOnCloseOrder(BaseOrderType):
     def place_instruction(self) -> dict:
         return limit_on_close_order(liability=self.liability, price=self.price)
 
+    @property
+    def info(self):
+        return {
+            "order_type": self.ORDER_TYPE.value,
+            "liability": self.liability,
+            "price": self.price,
+        }
+
 
 class MarketOnCloseOrder(BaseOrderType):
 
@@ -81,3 +106,10 @@ class MarketOnCloseOrder(BaseOrderType):
 
     def place_instruction(self) -> dict:
         return market_on_close_order(liability=self.liability)
+
+    @property
+    def info(self):
+        return {
+            "order_type": self.ORDER_TYPE.value,
+            "liability": self.liability,
+        }
diff --git a/flumine/order/process.py b/flumine/order/process.py
index 404ec77..987a55e 100644
--- a/flumine/order/process.py
+++ b/flumine/order/process.py
@@ -59,8 +59,8 @@ def process_current_order(order: BaseOrder):
         elif order.size_remaining == 0:
             order.execution_complete()
 
-    if order.trade.trade_complete:
-        order.trade.complete()
+    if order.trade.complete:
+        order.trade.complete_trade()
 
 
 def create_order_from_current(markets: Markets, strategies: Strategies, current_order):
diff --git a/flumine/order/trade.py b/flumine/order/trade.py
index 29a4bb1..0a74f53 100644
--- a/flumine/order/trade.py
+++ b/flumine/order/trade.py
@@ -1,5 +1,6 @@
 import uuid
 import logging
+import datetime
 import collections
 from enum import Enum
 from typing import Union, Type
@@ -48,6 +49,8 @@ class Trade:
         self.offset_orders = []  # pending offset orders once initial order has matched
         self.status_log = []
         self.status = TradeStatus.LIVE
+        self.date_time_created = datetime.datetime.utcnow()
+        self.date_time_complete = None
 
     # status
     def _update_status(self, status: TradeStatus) -> None:
@@ -55,8 +58,9 @@ class Trade:
         self.status = status
         logger.info("Trade status update: %s" % self.status.value, extra=self.info)
 
-    def complete(self) -> None:
+    def complete_trade(self) -> None:
         self._update_status(TradeStatus.COMPLETE)
+        self.date_time_complete = datetime.datetime.utcnow()
         # reset strategy context
         runner_context = self.strategy.get_runner_context(
             self.market_id, self.selection_id, self.handicap
@@ -64,13 +68,13 @@ class Trade:
         runner_context.reset()  # todo race condition?
 
     @property
-    def trade_complete(self) -> bool:
+    def complete(self) -> bool:
         if self.status != TradeStatus.LIVE:
             return False
         if self.offset_orders:
             return False
         for order in self.orders:
-            if order.status != OrderStatus.EXECUTION_COMPLETE:
+            if not order.complete:
                 return False
         return True
 
diff --git a/flumine/utils.py b/flumine/utils.py
index e3f1324..754e036 100644
--- a/flumine/utils.py
+++ b/flumine/utils.py
@@ -137,8 +137,8 @@ def wap(matched: list) -> Tuple[float, float]:
         return 0, 0
     a, b = 0, 0
     for match in matched:
-        a += match[0] * match[1]
-        b += match[1]
+        a += match[1] * match[2]
+        b += match[2]
     if b == 0 or a == 0:
         return 0, 0
     else:


Test patch:
diff --git a/flumine/backtest/simulated.py b/flumine/backtest/simulated.py
index 05e296d..9915ed1 100644
--- a/flumine/backtest/simulated.py
+++ b/flumine/backtest/simulated.py
@@ -19,7 +19,7 @@ class Simulated:
 
     def __init__(self, order):
         self.order = order
-        self.matched = []
+        self.matched = []  # [(publishTime, price, size)..]
         self.size_cancelled = 0.0
         self.size_lapsed = 0.0
         self.size_voided = 0.0
@@ -35,11 +35,13 @@ class Simulated:
             and market_book.bsp_reconciled
             and self.take_sp
         ):
-            self._process_sp(runner)
+            self._process_sp(market_book.publish_time_epoch, runner)
 
         if self.order.order_type.ORDER_TYPE == OrderTypes.LIMIT and self.size_remaining:
             # todo piq cancellations
-            self._process_traded(runner_analytics.traded)
+            self._process_traded(
+                market_book.publish_time_epoch, runner_analytics.traded
+            )
 
     def place(
         self, market_book: MarketBook, instruction: dict, bet_id: int
@@ -55,13 +57,21 @@ class Simulated:
             if self.order.side == "BACK":
                 if available_to_back >= price:
                     self._process_price_matched(
-                        price, size, runner.ex.available_to_back
+                        market_book.publish_time_epoch,
+                        price,
+                        size,
+                        runner.ex.available_to_back,
                     )
                     return self._create_place_response(bet_id)
                 available = runner.ex.available_to_lay
             else:
                 if available_to_lay <= price:
-                    self._process_price_matched(price, size, runner.ex.available_to_lay)
+                    self._process_price_matched(
+                        market_book.publish_time_epoch,
+                        price,
+                        size,
+                        runner.ex.available_to_lay,
+                    )
                     return self._create_place_response(bet_id)
                 available = runner.ex.available_to_back
 
@@ -125,7 +135,7 @@ class Simulated:
         return runner_dict.get((self.order.selection_id, self.order.handicap))
 
     def _process_price_matched(
-        self, price: float, size: float, available: list
+        self, publish_time: int, price: float, size: float, available: list
     ) -> None:
         # calculate matched on execution
         size_remaining = size
@@ -141,12 +151,12 @@ class Simulated:
                     _size_matched = _size_remaining
                 else:
                     _size_matched = avail["size"]
-                _matched = (avail["price"], round(_size_matched, 2))
+                _matched = (publish_time, avail["price"], round(_size_matched, 2))
                 self.matched.append(_matched)
             else:
                 break
 
-    def _process_sp(self, runner: RunnerBook) -> None:
+    def _process_sp(self, publish_time: int, runner: RunnerBook) -> None:
         # calculate matched on BSP reconciliation
         actual_sp = runner.sp.actual_sp
         if actual_sp:
@@ -170,18 +180,18 @@ class Simulated:
                     size = round(_order_type.liability / (actual_sp - 1), 2)
             else:
                 raise NotImplementedError()
-            self.matched.append((actual_sp, size))
+            self.matched.append((publish_time, actual_sp, size))
 
-    def _process_traded(self, traded: dict) -> None:
+    def _process_traded(self, publish_time: int, traded: dict) -> None:
         # calculate matched on MarketBook update
         price = self.order.order_type.price
         for traded_price, traded_size in traded.items():
             if self.side == "BACK" and traded_price >= price:
-                self._calculate_process_traded(traded_size)
+                self._calculate_process_traded(publish_time, traded_size)
             elif self.side == "LAY" and traded_price <= price:
-                self._calculate_process_traded(traded_size)
+                self._calculate_process_traded(publish_time, traded_size)
 
-    def _calculate_process_traded(self, traded_size: float) -> None:
+    def _calculate_process_traded(self, publish_time: int, traded_size: float) -> None:
         traded_size = traded_size / 2
         if self._piq - traded_size < 0:
             size = traded_size - self._piq
@@ -189,6 +199,7 @@ class Simulated:
             if size:
                 self.matched.append(
                     (
+                        publish_time,
                         self.order.order_type.price,
                         size,
                     )  # todo takes the worst price, i.e what was asked
diff --git a/tests/test_blotter.py b/tests/test_blotter.py
index 33a1659..1d7ec38 100644
--- a/tests/test_blotter.py
+++ b/tests/test_blotter.py
@@ -70,16 +70,14 @@ class BlotterTest(unittest.TestCase):
 
     def test_live_orders(self):
         self.assertFalse(self.blotter.live_orders)
-        mock_order = mock.Mock()
-        mock_order.status = OrderStatus.EXECUTABLE
+        mock_order = mock.Mock(complete=False)
         self.blotter._orders = {"12345": mock_order}
         self.assertTrue(self.blotter.live_orders)
 
     def test_live_orders_trade(self):
         self.assertFalse(self.blotter.live_orders)
         mock_order = mock.Mock()
-        mock_order.trade.trade_complete = False
-        mock_order.status = OrderStatus.EXECUTION_COMPLETE
+        mock_order.trade.complete = False
         self.blotter._orders = {"12345": mock_order}
         self.assertTrue(self.blotter.live_orders)
 
diff --git a/tests/test_clientcontrols.py b/tests/test_clientcontrols.py
index ad7c81d..2ac21b6 100644
--- a/tests/test_clientcontrols.py
+++ b/tests/test_clientcontrols.py
@@ -30,23 +30,17 @@ class TestBaseControl(unittest.TestCase):
         with self.assertRaises(NotImplementedError):
             self.control._validate(None)
 
-    # @mock.patch("flumine.controls.BaseControl.strategy_reset")
     def test_on_error(self):
         order = mock.Mock()
-        # order.flumine_order_type = "initial"
         order.info = {"hello": "world"}
-        self.control._on_error(order)
+        self.control._on_error(order, "test")
         order.violation.assert_called_with()
-        # mock_strategy_reset.assert_called_with(order)
 
-    # @mock.patch("flumine.controls.BaseControl.strategy_reset")
     def test_on_error_offset(self):
         order = mock.Mock()
-        # order.flumine_order_type = "offset"
         order.info = {"hello": "world"}
-        self.control._on_error(order)
+        self.control._on_error(order, "test")
         order.violation.assert_called_with()
-        # self.assertFalse(mock_strategy_reset.called)
 
 
 class TestMaxOrderCount(unittest.TestCase):
diff --git a/tests/test_execution.py b/tests/test_execution.py
index 83842e0..ec8b487 100644
--- a/tests/test_execution.py
+++ b/tests/test_execution.py
@@ -90,7 +90,7 @@ class BaseExecutionTest(unittest.TestCase):
         with self.assertRaises(NotImplementedError):
             self.execution.execute_place(None, None)
 
-    def test__order_logger(self):
+    def test__order_logger_place(self):
         mock_order = mock.Mock()
         mock_instruction_report = mock.Mock()
         self.execution._order_logger(
@@ -98,29 +98,37 @@ class BaseExecutionTest(unittest.TestCase):
         )
         self.assertEqual(mock_order.bet_id, mock_instruction_report.bet_id)
         mock_order.responses.placed.assert_called_with(mock_instruction_report)
+        self.mock_flumine.log_control.assert_called_with(mock_order)
 
+    def test__order_logger_cancel(self):
+        mock_order = mock.Mock()
+        mock_instruction_report = mock.Mock()
         self.execution._order_logger(
             mock_order, mock_instruction_report, OrderPackageType.CANCEL
         )
         mock_order.responses.cancelled.assert_called_with(mock_instruction_report)
 
+    def test__order_logger_update(self):
+        mock_order = mock.Mock()
+        mock_instruction_report = mock.Mock()
         self.execution._order_logger(
             mock_order, mock_instruction_report, OrderPackageType.UPDATE
         )
         mock_order.responses.updated.assert_called_with(mock_instruction_report)
 
+    def test__order_logger_replace(self):
+        mock_order = mock.Mock()
+        mock_instruction_report = mock.Mock()
         self.execution._order_logger(
             mock_order, mock_instruction_report, OrderPackageType.REPLACE
         )
         self.assertEqual(mock_order.bet_id, mock_instruction_report.bet_id)
         mock_order.responses.placed.assert_called_with(mock_instruction_report)
+        self.mock_flumine.log_control.assert_called_with(mock_order)
 
     def test_handler_queue(self):
         self.assertEqual(self.execution.handler_queue, self.mock_flumine.handler_queue)
 
-    def test_markets(self):
-        self.assertEqual(self.execution.markets, self.mock_flumine.markets)
-
     def test_shutdown(self):
         self.execution.shutdown()
         self.assertTrue(self.execution._thread_pool._shutdown)
diff --git a/tests/test_markets.py b/tests/test_markets.py
index 7e4a37c..b0a40bb 100644
--- a/tests/test_markets.py
+++ b/tests/test_markets.py
@@ -125,7 +125,7 @@ class MarketTest(unittest.TestCase):
         self.market.place_order(mock_order)
         mock_order.place.assert_called_with(self.market.market_book.publish_time)
         self.assertEqual(self.market.blotter.pending_place, [mock_order])
-        self.mock_flumine.log_control.assert_called_with(mock_events.OrderEvent())
+        self.mock_flumine.log_control.assert_called_with(mock_events.TradeEvent())
 
     @mock.patch("flumine.markets.market.events")
     def test_place_order_not_executed(self, mock_events):
@@ -134,7 +134,7 @@ class MarketTest(unittest.TestCase):
         self.market.place_order(mock_order, execute=False)
         mock_order.place.assert_called_with(self.market.market_book.publish_time)
         self.assertEqual(self.market.blotter.pending_place, [])
-        self.mock_flumine.log_control.assert_called_with(mock_events.OrderEvent())
+        self.mock_flumine.log_control.assert_called_with(mock_events.TradeEvent())
 
     def test_place_order_retry(self):
         mock_order = mock.Mock()
diff --git a/tests/test_order.py b/tests/test_order.py
index 8594152..d847806 100644
--- a/tests/test_order.py
+++ b/tests/test_order.py
@@ -31,8 +31,11 @@ class BaseOrderTest(unittest.TestCase):
         self.assertIsNone(self.order.EXCHANGE)
         self.assertEqual(self.order.update_data, {})
         self.assertIsNone(self.order.publish_time)
+        self.assertIsNotNone(self.order.date_time_created)
+        self.assertIsNone(self.order.date_time_execution_complete)
 
-    def test__update_status(self):
+    @mock.patch("flumine.order.order.BaseOrder.info")
+    def test__update_status(self, mock_info):
         self.order._update_status(OrderStatus.EXECUTION_COMPLETE)
         self.assertEqual(self.order.status_log, [OrderStatus.EXECUTION_COMPLETE])
         self.assertEqual(self.order.status, OrderStatus.EXECUTION_COMPLETE)
@@ -54,6 +57,7 @@ class BaseOrderTest(unittest.TestCase):
         self.order.update_data = {123: 456}
         self.order.execution_complete()
         mock__update_status.assert_called_with(OrderStatus.EXECUTION_COMPLETE)
+        self.assertIsNotNone(self.order.date_time_execution_complete)
         self.assertEqual(self.order.update_data, {})
 
     @mock.patch("flumine.order.order.BaseOrder._update_status")
@@ -142,6 +146,28 @@ class BaseOrderTest(unittest.TestCase):
         self.order.simulated = True
         self.assertTrue(self.order.current_order)
 
+    def test_complete(self):
+        self.order.status = None
+        self.assertFalse(self.order.complete)
+        for s in [
+            OrderStatus.PENDING,
+            OrderStatus.CANCELLING,
+            OrderStatus.UPDATING,
+            OrderStatus.REPLACING,
+            OrderStatus.EXECUTABLE,
+        ]:
+            self.order.status = s
+            self.assertFalse(self.order.complete)
+        for s in [
+            OrderStatus.EXECUTION_COMPLETE,
+            OrderStatus.EXPIRED,
+            OrderStatus.VOIDED,
+            OrderStatus.LAPSED,
+            OrderStatus.VIOLATION,
+        ]:
+            self.order.status = s
+            self.assertTrue(self.order.complete)
+
     def test_average_price_matched(self):
         with self.assertRaises(NotImplementedError):
             assert self.order.average_price_matched
@@ -185,23 +211,6 @@ class BaseOrderTest(unittest.TestCase):
             (self.mock_trade.market_id, self.mock_trade.selection_id, 1),
         )
 
-    def test_info(self):
-        self.order.status_log = [OrderStatus.PENDING, OrderStatus.EXECUTION_COMPLETE]
-        self.assertEqual(
-            self.order.info,
-            {
-                "bet_id": None,
-                "handicap": 1,
-                "id": self.order.id,
-                "market_id": self.mock_trade.market_id,
-                "selection_id": self.mock_trade.selection_id,
-                "status": None,
-                "status_log": "Pending, Execution complete",
-                "trade": self.mock_trade.info,
-                "customer_order_ref": self.order.customer_order_ref,
-            },
-        )
-
     def test_repr(self):
         self.assertEqual(repr(self.order), "Order None: None")
 
@@ -395,3 +404,30 @@ class BetfairOrderTest(unittest.TestCase):
         mock_current_order = mock.Mock(size_voided=10)
         self.order.responses.current_order = mock_current_order
         self.assertEqual(self.order.size_voided, mock_current_order.size_voided)
+
+    def test_info(self):
+        self.order.status_log = [OrderStatus.PENDING, OrderStatus.EXECUTION_COMPLETE]
+        self.assertEqual(
+            self.order.info,
+            {
+                "bet_id": None,
+                "handicap": self.order.handicap,
+                "id": self.order.id,
+                "market_id": self.mock_trade.market_id,
+                "selection_id": self.mock_trade.selection_id,
+                "status": None,
+                "status_log": "Pending, Execution complete",
+                "trade": self.mock_trade.info,
+                "order_type": self.mock_order_type.info,
+                "info": {
+                    "side": self.order.side,
+                    "size_matched": self.order.size_matched,
+                    "size_remaining": self.order.size_remaining,
+                    "size_cancelled": self.order.size_cancelled,
+                    "size_lapsed": self.order.size_lapsed,
+                    "size_voided": self.order.size_voided,
+                    "average_price_matched": self.order.average_price_matched,
+                },
+                "customer_order_ref": self.order.customer_order_ref,
+            },
+        )
diff --git a/tests/test_ordertype.py b/tests/test_ordertype.py
index 74a3136..5167263 100644
--- a/tests/test_ordertype.py
+++ b/tests/test_ordertype.py
@@ -23,6 +23,10 @@ class BaseOrderTypeTest(unittest.TestCase):
         with self.assertRaises(NotImplementedError):
             self.order_type.place_instruction()
 
+    def test_info(self):
+        with self.assertRaises(NotImplementedError):
+            assert self.order_type.info
+
 
 class LimitOrderTest(unittest.TestCase):
     def setUp(self) -> None:
@@ -53,6 +57,21 @@ class LimitOrderTest(unittest.TestCase):
             },
         )
 
+    def test_info(self):
+        self.assertEqual(
+            self.order_type.info,
+            {
+                "bet_target_size": 2,
+                "bet_target_type": "NO",
+                "min_fill_size": 34.5,
+                "order_type": "Limit",
+                "persistence_type": "PERSIST",
+                "price": 1.01,
+                "size": 2,
+                "time_in_force": "YES",
+            },
+        )
+
 
 class LimitOnCloseOrderTest(unittest.TestCase):
     def setUp(self) -> None:
@@ -69,6 +88,12 @@ class LimitOnCloseOrderTest(unittest.TestCase):
             self.order_type.place_instruction(), {"price": 1.01, "liability": 64}
         )
 
+    def test_info(self):
+        self.assertEqual(
+            self.order_type.info,
+            {"liability": 64, "order_type": "Limit on close", "price": 1.01},
+        )
+
 
 class MarketOnCloseOrderTest(unittest.TestCase):
     def setUp(self) -> None:
@@ -81,3 +106,8 @@ class MarketOnCloseOrderTest(unittest.TestCase):
 
     def test_place_instruction(self):
         self.assertEqual(self.order_type.place_instruction(), {"liability": 128})
+
+    def test_info(self):
+        self.assertEqual(
+            self.order_type.info, {"liability": 128, "order_type": "Market on close"}
+        )
diff --git a/tests/test_simulated.py b/tests/test_simulated.py
index c1a5019..964661a 100644
--- a/tests/test_simulated.py
+++ b/tests/test_simulated.py
@@ -33,8 +33,12 @@ class SimulatedTest(unittest.TestCase):
         mock_market_book.bsp_reconciled = True
         mock_runner_analytics = mock.Mock()
         self.simulated(mock_market_book, mock_runner_analytics)
-        mock__process_sp.assert_called_with(mock__get_runner())
-        mock__process_traded.assert_called_with(mock_runner_analytics.traded)
+        mock__process_sp.assert_called_with(
+            mock_market_book.publish_time_epoch, mock__get_runner()
+        )
+        mock__process_traded.assert_called_with(
+            mock_market_book.publish_time_epoch, mock_runner_analytics.traded
+        )
 
     @mock.patch("flumine.backtest.simulated.Simulated._get_runner")
     @mock.patch("flumine.backtest.simulated.Simulated.take_sp", return_value=True)
@@ -52,7 +56,9 @@ class SimulatedTest(unittest.TestCase):
 
         mock_market_book.bsp_reconciled = True
         self.simulated(mock_market_book, {})
-        mock__process_sp.assert_called_with(mock__get_runner())
+        mock__process_sp.assert_called_with(
+            mock_market_book.publish_time_epoch, mock__get_runner()
+        )
         mock__process_traded.assert_not_called()
 
     @mock.patch("flumine.backtest.simulated.Simulated._get_runner")
@@ -80,7 +86,9 @@ class SimulatedTest(unittest.TestCase):
         resp = self.simulated.place(mock_market_book, {}, 1)
         self.assertEqual(resp.average_price_matched, 12)
         self.assertEqual(resp.size_matched, 2)
-        self.assertEqual(self.simulated.matched, [(12, 2)])
+        self.assertEqual(
+            self.simulated.matched, [(mock_market_book.publish_time_epoch, 12, 2)]
+        )
 
     @mock.patch("flumine.backtest.simulated.Simulated._get_runner")
     def test_place_limit_back_unmatched(self, mock__get_runner):
@@ -111,7 +119,9 @@ class SimulatedTest(unittest.TestCase):
         resp = self.simulated.place(mock_market_book, {}, 1)
         self.assertEqual(resp.average_price_matched, 12)
         self.assertEqual(resp.size_matched, 2)
-        self.assertEqual(self.simulated.matched, [(12, 2)])
+        self.assertEqual(
+            self.simulated.matched, [(mock_market_book.publish_time_epoch, 12, 2)]
+        )
 
     @mock.patch("flumine.backtest.simulated.Simulated._get_runner")
     def test_place_limit_lay_unmatched(self, mock__get_runner):
@@ -185,23 +195,25 @@ class SimulatedTest(unittest.TestCase):
         return_value="BACK",
     )
     def test__process_price_matched_back(self, mock_side):
-        self.simulated._process_price_matched(12.0, 2.00, [{"price": 15, "size": 120}])
-        self.assertEqual(self.simulated.matched, [(15, 2)])
+        self.simulated._process_price_matched(
+            1234567, 12.0, 2.00, [{"price": 15, "size": 120}]
+        )
+        self.assertEqual(self.simulated.matched, [(1234567, 15, 2)])
         self.simulated.matched = []
         self.simulated._process_price_matched(
-            12.0, 2.00, [{"price": 15, "size": 1}, {"price": 12, "size": 1}]
+            1234568, 12.0, 2.00, [{"price": 15, "size": 1}, {"price": 12, "size": 1}]
         )
-        self.assertEqual(self.simulated.matched, [(15, 1), (12, 1)])
+        self.assertEqual(self.simulated.matched, [(1234568, 15, 1), (1234568, 12, 1)])
         self.simulated.matched = []
         self.simulated._process_price_matched(
-            12.0, 2.00, [{"price": 15, "size": 1}, {"price": 12, "size": 0.5}]
+            1234569, 12.0, 2.00, [{"price": 15, "size": 1}, {"price": 12, "size": 0.5}]
         )
-        self.assertEqual(self.simulated.matched, [(15, 1), (12, 0.5)])
+        self.assertEqual(self.simulated.matched, [(1234569, 15, 1), (1234569, 12, 0.5)])
         self.simulated.matched = []
         self.simulated._process_price_matched(
-            12.0, 2.00, [{"price": 15, "size": 1}, {"price": 11, "size": 0.5}]
+            1234570, 12.0, 2.00, [{"price": 15, "size": 1}, {"price": 11, "size": 0.5}]
         )
-        self.assertEqual(self.simulated.matched, [(15, 1)])
+        self.assertEqual(self.simulated.matched, [(1234570, 15, 1)])
 
     @mock.patch(
         "flumine.backtest.simulated.Simulated.side",
@@ -210,45 +222,52 @@ class SimulatedTest(unittest.TestCase):
     )
     def test__process_price_matched_lay(self, mock_side):
         self.simulated._process_price_matched(
-            3.0, 20.00, [{"price": 2.02, "size": 120}]
+            1234571, 3.0, 20.00, [{"price": 2.02, "size": 120}]
         )
-        self.assertEqual(self.simulated.matched, [(2.02, 20)])
+        self.assertEqual(self.simulated.matched, [(1234571, 2.02, 20)])
         self.simulated.matched = []
         self.simulated._process_price_matched(
-            3.0, 20.00, [{"price": 2.02, "size": 1}, {"price": 3, "size": 20}]
+            1234571, 3.0, 20.00, [{"price": 2.02, "size": 1}, {"price": 3, "size": 20}]
         )
-        self.assertEqual(self.simulated.matched, [(2.02, 1), (3, 19)])
+        self.assertEqual(self.simulated.matched, [(1234571, 2.02, 1), (1234571, 3, 19)])
         self.simulated.matched = []
         self.simulated._process_price_matched(
-            3.0, 20.00, [{"price": 2.02, "size": 1}, {"price": 2.9, "size": 0.5}]
+            1234571,
+            3.0,
+            20.00,
+            [{"price": 2.02, "size": 1}, {"price": 2.9, "size": 0.5}],
+        )
+        self.assertEqual(
+            self.simulated.matched, [(1234571, 2.02, 1), (1234571, 2.9, 0.5)]
         )
-        self.assertEqual(self.simulated.matched, [(2.02, 1), (2.9, 0.5)])
         self.simulated.matched = []
         self.simulated._process_price_matched(
-            3.0, 20.00, [{"price": 3, "size": 1}, {"price": 11, "size": 0.5}]
+            1234571, 3.0, 20.00, [{"price": 3, "size": 1}, {"price": 11, "size": 0.5}]
         )
-        self.assertEqual(self.simulated.matched, [(3, 1)])
+        self.assertEqual(self.simulated.matched, [(1234571, 3, 1)])
 
     def test__process_sp(self):
         mock_runner = mock.Mock()
         mock_runner.sp.actual_sp = 12.20
-        self.simulated._process_sp(mock_runner)
-        self.assertEqual(self.simulated.matched, [(12.2, 2.00)])
+        self.simulated._process_sp(1234571, mock_runner)
+        self.assertEqual(self.simulated.matched, [(1234571, 12.2, 2.00)])
         self.assertTrue(self.simulated._bsp_reconciled)
 
     def test__process_sp_none(self):
         mock_runner = mock.Mock()
         mock_runner.sp.actual_sp = None
-        self.simulated._process_sp(mock_runner)
+        self.simulated._process_sp(1234571, mock_runner)
         self.assertEqual(self.simulated.matched, [])
         self.assertFalse(self.simulated._bsp_reconciled)
 
     def test__process_sp_processed_semi(self):
         mock_runner = mock.Mock()
         mock_runner.sp.actual_sp = 12.20
-        self.simulated.matched = [(10.0, 1)]
-        self.simulated._process_sp(mock_runner)
-        self.assertEqual(self.simulated.matched, [(10.0, 1), (12.2, 1)])
+        self.simulated.matched = [(1234571, 10.0, 1)]
+        self.simulated._process_sp(1234572, mock_runner)
+        self.assertEqual(
+            self.simulated.matched, [(1234571, 10.0, 1), (1234572, 12.2, 1)]
+        )
         self.assertTrue(self.simulated._bsp_reconciled)
 
     def test__process_sp_limit_on_close_back(self):
@@ -257,8 +276,8 @@ class SimulatedTest(unittest.TestCase):
         self.simulated.order.order_type = mock_limit_on_close_order
         mock_runner_book = mock.Mock()
         mock_runner_book.sp.actual_sp = 69
-        self.simulated._process_sp(mock_runner_book)
-        self.assertEqual(self.simulated.matched, [(69, 2.00)])
+        self.simulated._process_sp(1234573, mock_runner_book)
+        self.assertEqual(self.simulated.matched, [(1234573, 69, 2.00)])
         self.assertTrue(self.simulated._bsp_reconciled)
 
     def test__process_sp_limit_on_close_back_no_match(self):
@@ -267,7 +286,7 @@ class SimulatedTest(unittest.TestCase):
         self.simulated.order.order_type = mock_limit_on_close_order
         mock_runner_book = mock.Mock()
         mock_runner_book.sp.actual_sp = 8
-        self.simulated._process_sp(mock_runner_book)
+        self.simulated._process_sp(1234574, mock_runner_book)
         self.assertEqual(self.simulated.matched, [])
         self.assertTrue(self.simulated._bsp_reconciled)
 
@@ -278,8 +297,8 @@ class SimulatedTest(unittest.TestCase):
         self.simulated.order.side = "LAY"
         mock_runner_book = mock.Mock()
         mock_runner_book.sp.actual_sp = 69
-        self.simulated._process_sp(mock_runner_book)
-        self.assertEqual(self.simulated.matched, [(69, 0.03)])
+        self.simulated._process_sp(1234575, mock_runner_book)
+        self.assertEqual(self.simulated.matched, [(1234575, 69, 0.03)])
         self.assertTrue(self.simulated._bsp_reconciled)
 
     def test__process_sp_limit_on_close_lay_no_match(self):
@@ -289,7 +308,7 @@ class SimulatedTest(unittest.TestCase):
         self.simulated.order.side = "LAY"
         mock_runner_book = mock.Mock()
         mock_runner_book.sp.actual_sp = 69
-        self.simulated._process_sp(mock_runner_book)
+        self.simulated._process_sp(1234576, mock_runner_book)
         self.assertEqual(self.simulated.matched, [])
         self.assertTrue(self.simulated._bsp_reconciled)
 
@@ -299,8 +318,8 @@ class SimulatedTest(unittest.TestCase):
         self.simulated.order.order_type = mock_limit_on_close_order
         mock_runner_book = mock.Mock()
         mock_runner_book.sp.actual_sp = 69
-        self.simulated._process_sp(mock_runner_book)
-        self.assertEqual(self.simulated.matched, [(69, 2.00)])
+        self.simulated._process_sp(1234577, mock_runner_book)
+        self.assertEqual(self.simulated.matched, [(1234577, 69, 2.00)])
         self.assertTrue(self.simulated._bsp_reconciled)
 
     def test__process_sp_market_on_close_lay(self):
@@ -310,46 +329,48 @@ class SimulatedTest(unittest.TestCase):
         self.simulated.order.side = "LAY"
         mock_runner_book = mock.Mock()
         mock_runner_book.sp.actual_sp = 69
-        self.simulated._process_sp(mock_runner_book)
-        self.assertEqual(self.simulated.matched, [(69, 0.03)])
+        self.simulated._process_sp(1234578, mock_runner_book)
+        self.assertEqual(self.simulated.matched, [(1234578, 69, 0.03)])
         self.assertTrue(self.simulated._bsp_reconciled)
 
     @mock.patch("flumine.backtest.simulated.Simulated._calculate_process_traded")
     def test__process_traded_back(self, mock__calculate_process_traded):
-        self.simulated._process_traded({12: 120})
-        mock__calculate_process_traded.assert_called_with(120)
+        self.simulated._process_traded(1234579, {12: 120})
+        mock__calculate_process_traded.assert_called_with(1234579, 120)
 
     @mock.patch("flumine.backtest.simulated.Simulated._calculate_process_traded")
     def test__process_traded_back_no(self, mock__calculate_process_traded):
-        self.simulated._process_traded({11: 120})
+        self.simulated._process_traded(1234580, {11: 120})
         mock__calculate_process_traded.assert_not_called()
 
     @mock.patch("flumine.backtest.simulated.Simulated._calculate_process_traded")
     def test__process_traded_lay(self, mock__calculate_process_traded):
         self.simulated.order.side = "LAY"
-        self.simulated._process_traded({12: 120})
-        mock__calculate_process_traded.assert_called_with(120)
+        self.simulated._process_traded(1234581, {12: 120})
+        mock__calculate_process_traded.assert_called_with(1234581, 120)
 
     @mock.patch("flumine.backtest.simulated.Simulated._calculate_process_traded")
     def test__process_traded_lay_no(self, mock__calculate_process_traded):
         self.simulated.order.side = "LAY"
-        self.simulated._process_traded({13: 120})
+        self.simulated._process_traded(1234582, {13: 120})
         mock__calculate_process_traded.assert_not_called()
 
     def test__calculate_process_traded(self):
-        self.simulated._calculate_process_traded(2.00)
-        self.simulated._calculate_process_traded(2.00)
-        self.simulated._calculate_process_traded(2.00)
-        self.assertEqual(self.simulated.matched, [(12, 1.00), (12, 1.00)])
+        self.simulated._calculate_process_traded(1234582, 2.00)
+        self.simulated._calculate_process_traded(1234583, 2.00)
+        self.simulated._calculate_process_traded(1234584, 2.00)
+        self.assertEqual(
+            self.simulated.matched, [(1234582, 12, 1.00), (1234583, 12, 1.00)]
+        )
         self.assertEqual(self.simulated._piq, 0)
 
     def test__calculate_process_traded_piq(self):
         self.simulated._piq = 2.00
-        self.simulated._calculate_process_traded(4.00)
+        self.simulated._calculate_process_traded(1234585, 4.00)
         self.assertEqual(self.simulated.matched, [])
         self.assertEqual(self.simulated._piq, 0)
-        self.simulated._calculate_process_traded(4.00)
-        self.assertEqual(self.simulated.matched, [(12, 2.00)])
+        self.simulated._calculate_process_traded(1234586, 4.00)
+        self.assertEqual(self.simulated.matched, [(1234586, 12, 2.00)])
         self.assertEqual(self.simulated._piq, 0)
 
     def test_take_sp(self):
@@ -367,12 +388,12 @@ class SimulatedTest(unittest.TestCase):
 
     def test_average_price_matched(self):
         self.assertEqual(self.simulated.average_price_matched, 0)
-        self.simulated.matched = [(1, 2)]
+        self.simulated.matched = [(1234, 1, 2)]
         self.assertEqual(self.simulated.average_price_matched, 1)
 
     def test_size_matched(self):
         self.assertEqual(self.simulated.size_matched, 0)
-        self.simulated.matched = [(1, 2)]
+        self.simulated.matched = [(4321, 1, 2)]
         self.assertEqual(self.simulated.size_matched, 2)
 
     @mock.patch(
diff --git a/tests/test_trade.py b/tests/test_trade.py
index 66c72cc..e8cc3b3 100644
--- a/tests/test_trade.py
+++ b/tests/test_trade.py
@@ -40,6 +40,8 @@ class TradeTest(unittest.TestCase):
         self.assertEqual(self.trade.status_log, [])
         self.assertEqual(self.trade.orders, [])
         self.assertEqual(self.trade.offset_orders, [])
+        self.assertIsNotNone(self.trade.date_time_created)
+        self.assertIsNone(self.trade.date_time_complete)
 
     def test__update_status(self):
         self.trade._update_status(TradeStatus.COMPLETE)
@@ -47,30 +49,31 @@ class TradeTest(unittest.TestCase):
         self.assertEqual(self.trade.status, TradeStatus.COMPLETE)
 
     @mock.patch("flumine.order.trade.Trade._update_status")
-    def test_complete(self, mock__update_status):
-        self.trade.complete()
+    def test_complete_trade(self, mock__update_status):
+        self.trade.complete_trade()
         mock__update_status.assert_called_with(TradeStatus.COMPLETE)
         runner_context = self.mock_strategy.get_runner_context(
             self.trade.market_id, self.trade.selection_id, self.trade.handicap
         )
         runner_context.reset.assert_called_with()
+        self.assertIsNotNone(self.trade.date_time_complete)
 
-    def test_trade_complete(self):
-        self.assertTrue(self.trade.trade_complete)
-        mock_order = mock.Mock(status=OrderStatus.PENDING)
+    def test_complete(self):
+        self.assertTrue(self.trade.complete)
+        mock_order = mock.Mock(complete=False)
         self.trade.orders.append(mock_order)
-        self.assertFalse(self.trade.trade_complete)
+        self.assertFalse(self.trade.complete)
 
     def test_trade_complete_offset(self):
         self.trade.offset_orders = [1]
-        self.assertFalse(self.trade.trade_complete)
+        self.assertFalse(self.trade.complete)
 
     def test_trade_complete_replace_order(self):
-        self.assertTrue(self.trade.trade_complete)
-        mock_order = mock.Mock(status=OrderStatus.EXECUTION_COMPLETE)
+        self.assertTrue(self.trade.complete)
+        mock_order = mock.Mock(complete=True)
         self.trade.status = TradeStatus.COMPLETE
         self.trade.orders.append(mock_order)
-        self.assertFalse(self.trade.trade_complete)
+        self.assertFalse(self.trade.complete)
 
     def test_create_order(self):
         mock_order_type = mock.Mock()
diff --git a/tests/test_tradingcontrols.py b/tests/test_tradingcontrols.py
index 02d5259..7725c73 100644
--- a/tests/test_tradingcontrols.py
+++ b/tests/test_tradingcontrols.py
@@ -102,7 +102,7 @@ class TestOrderValidation(unittest.TestCase):
         order = mock.Mock()
         order.order_type.ORDER_TYPE = "RUSH"
         self.trading_control._validate_betfair_order(order)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(order, "Unknown orderType")
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_size(self, mock_on_error):
@@ -116,21 +116,21 @@ class TestOrderValidation(unittest.TestCase):
         order = mock.Mock()
         order.order_type.size = None
         self.trading_control._validate_betfair_size(order)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(order, "Order size is None")
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_size_on_error_two(self, mock_on_error):
         order = mock.Mock()
         order.order_type.size = -1
         self.trading_control._validate_betfair_size(order)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(order, "Order size is less than 0")
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_size_on_error_three(self, mock_on_error):
         order = mock.Mock()
         order.order_type.size = 1.999
         self.trading_control._validate_betfair_size(order)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(order, "Order size has more than 2dp")
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_price(self, mock_on_error):
@@ -144,14 +144,14 @@ class TestOrderValidation(unittest.TestCase):
         order = mock.Mock()
         order.order_type.price = None
         self.trading_control._validate_betfair_price(order)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(order, "Order price is None")
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_price_on_error_two(self, mock_on_error):
         order = mock.Mock()
         order.order_type.price = -1
         self.trading_control._validate_betfair_price(order)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(order, "Order price is not valid")
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_liability(self, mock_on_error):
@@ -165,14 +165,14 @@ class TestOrderValidation(unittest.TestCase):
         order = mock.Mock()
         order.order_type.liability = None
         self.trading_control._validate_betfair_liability(order)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(order, "Order liability is None")
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_liability_on_error_two(self, mock_on_error):
         order = mock.Mock()
         order.order_type.liability = -1
         self.trading_control._validate_betfair_liability(order)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(order, "Order liability is less than 0")
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_min_size_limit(self, mock_on_error):
@@ -205,7 +205,10 @@ class TestOrderValidation(unittest.TestCase):
         order.order_type.size = 1.99
         order.order_type.price = 2
         self.trading_control._validate_betfair_min_size(order, OrderTypes.LIMIT)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(
+            order,
+            "Order size is less than min bet size (2) or payout (10) for currency",
+        )
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_min_size(self, mock_on_error):
@@ -238,7 +241,9 @@ class TestOrderValidation(unittest.TestCase):
         self.trading_control._validate_betfair_min_size(
             order, OrderTypes.LIMIT_ON_CLOSE
         )
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(
+            order, "Liability is less than min bet size (2) for currency"
+        )
 
     @mock.patch("flumine.controls.tradingcontrols.OrderValidation._on_error")
     def test__validate_betfair_min_size_on_error_two(self, mock_on_error):
@@ -249,7 +254,9 @@ class TestOrderValidation(unittest.TestCase):
         self.trading_control._validate_betfair_min_size(
             order, OrderTypes.LIMIT_ON_CLOSE
         )
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(
+            order, "Liability is less than min BSP payout (10) for currency"
+        )
 
 
 class TestStrategyExposure(unittest.TestCase):
@@ -272,7 +279,10 @@ class TestStrategyExposure(unittest.TestCase):
         order_package.package_type = OrderPackageType.PLACE
         order_package.__iter__ = mock.Mock(return_value=iter([order]))
         self.trading_control._validate(order_package)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(
+            order,
+            "Order exposure (12) is greater than strategy.max_order_strategy (10)",
+        )
 
     @mock.patch("flumine.controls.tradingcontrols.StrategyExposure._on_error")
     def test_validate_limit_on_close(self, mock_on_error):
@@ -285,7 +295,10 @@ class TestStrategyExposure(unittest.TestCase):
         order_package.package_type = OrderPackageType.PLACE
         order_package.__iter__ = mock.Mock(return_value=iter([order]))
         self.trading_control._validate(order_package)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(
+            order,
+            "Order exposure (12) is greater than strategy.max_order_strategy (10)",
+        )
 
     @mock.patch("flumine.controls.tradingcontrols.StrategyExposure._on_error")
     def test_validate_market_on_close(self, mock_on_error):
@@ -298,7 +311,10 @@ class TestStrategyExposure(unittest.TestCase):
         order_package.package_type = OrderPackageType.PLACE
         order_package.__iter__ = mock.Mock(return_value=iter([order]))
         self.trading_control._validate(order_package)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(
+            order,
+            "Order exposure (12) is greater than strategy.max_order_strategy (10)",
+        )
 
     @mock.patch("flumine.controls.tradingcontrols.StrategyExposure._on_error")
     def test_validate_selection(self, mock_on_error):
@@ -318,4 +334,7 @@ class TestStrategyExposure(unittest.TestCase):
         order_package.__iter__ = mock.Mock(return_value=iter([order]))
 
         self.trading_control._validate(order_package)
-        mock_on_error.assert_called_with(order)
+        mock_on_error.assert_called_with(
+            order,
+            "Potential selection exposure (-12) is greater than strategy.max_selection_exposure (10)",
+        )
diff --git a/tests/test_utils.py b/tests/test_utils.py
index f9d4fab..85892bb 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -108,9 +108,11 @@ class UtilsTest(unittest.TestCase):
         self.assertEqual(utils.calculate_exposure([(5.6, 200)], [(5.6, 100)]), -100)
 
     def test_wap(self):
-        self.assertEqual(utils.wap([(1.5, 100), (1.6, 100)]), (200, 1.55))
+        self.assertEqual(
+            utils.wap([(123456789, 1.5, 100), (123456789, 1.6, 100)]), (200, 1.55)
+        )
         self.assertEqual(utils.wap([]), (0, 0))
-        self.assertEqual(utils.wap([(1.5, 0)]), (0, 0))
+        self.assertEqual(utils.wap([(123456789, 1.5, 0)]), (0, 0))
 
     def test_call_check_market(self):
         mock_strategy_check = mock.Mock()