From 110aebcf5a6b9403e1245159efc06336943b790e Mon Sep 17 00:00:00 2001 From: Maurice Berk Date: Fri, 24 Jan 2025 08:49:40 +0000 Subject: [PATCH 1/7] Add container types --- flumine/markets/blotter.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flumine/markets/blotter.py b/flumine/markets/blotter.py index bed6f158..e1f3ea52 100644 --- a/flumine/markets/blotter.py +++ b/flumine/markets/blotter.py @@ -61,7 +61,7 @@ def strategy_orders( strategy, order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, - ) -> list: + ) -> list[BaseOrder]: """Returns all orders related to a strategy.""" orders = self._strategy_orders[strategy] if order_status: @@ -77,7 +77,7 @@ def strategy_selection_orders( handicap: float = 0, order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, - ) -> list: + ) -> list[BaseOrder]: """Returns all orders related to a strategy selection.""" orders = self._strategy_selection_orders[(strategy, selection_id, handicap)] if order_status: @@ -91,7 +91,7 @@ def client_orders( client, order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, - ) -> list: + ) -> list[BaseOrder]: orders = self._client_orders[client] if order_status: orders = [o for o in orders if o.status in order_status] @@ -105,7 +105,7 @@ def client_strategy_orders( strategy, order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, - ) -> list: + ) -> list[BaseOrder]: orders = self._client_strategy_orders[(client, strategy)] if order_status: orders = [o for o in orders if o.status in order_status] @@ -114,7 +114,7 @@ def client_strategy_orders( return orders @property - def live_orders(self) -> Iterable: + def live_orders(self) -> Iterable[BaseOrder]: return iter(list(self._live_orders)) @property @@ -152,7 +152,7 @@ def process_closed_market(self, market, market_book) -> None: "line_range_result unavailable, for simulation results update the market.context['line_range_result']" ) - def process_cleared_orders(self, cleared_orders) -> list: + def process_cleared_orders(self, cleared_orders) -> list[BaseOrder]: for cleared_order in cleared_orders.orders: order_id = cleared_order.customer_order_ref[STRATEGY_NAME_HASH_LENGTH + 1 :] if order_id in self: From 6351d3dc4a5c3c2728add328edb65b2b6caeb2af Mon Sep 17 00:00:00 2001 From: Maurice Berk Date: Fri, 24 Jan 2025 08:51:09 +0000 Subject: [PATCH 2/7] Type strategy --- flumine/markets/blotter.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/flumine/markets/blotter.py b/flumine/markets/blotter.py index e1f3ea52..df365798 100644 --- a/flumine/markets/blotter.py +++ b/flumine/markets/blotter.py @@ -9,6 +9,7 @@ STRATEGY_NAME_HASH_LENGTH, ) from ..order.order import BaseOrder, OrderStatus +from ..strategy.strategy import BaseStrategy logger = logging.getLogger(__name__) @@ -58,7 +59,7 @@ def get_order_bet_id(self, bet_id: str) -> Optional[BaseOrder]: def strategy_orders( self, - strategy, + strategy: BaseStrategy, order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, ) -> list[BaseOrder]: @@ -72,7 +73,7 @@ def strategy_orders( def strategy_selection_orders( self, - strategy, + strategy: BaseStrategy, selection_id: int, handicap: float = 0, order_status: Optional[List[OrderStatus]] = None, @@ -102,7 +103,7 @@ def client_orders( def client_strategy_orders( self, client, - strategy, + strategy: BaseStrategy, order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, ) -> list[BaseOrder]: From fb3274ff3174030bb7ce160807208a4bc385b173 Mon Sep 17 00:00:00 2001 From: Maurice Berk Date: Fri, 24 Jan 2025 08:54:41 +0000 Subject: [PATCH 3/7] Add more type hints --- flumine/markets/blotter.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/flumine/markets/blotter.py b/flumine/markets/blotter.py index df365798..f1bda90e 100644 --- a/flumine/markets/blotter.py +++ b/flumine/markets/blotter.py @@ -2,6 +2,9 @@ from typing import Iterable, Optional, List from collections import defaultdict +from betfairlightweight.resources.bettingresources import MarketBook + +from ..markets.market import Market from ..order.ordertype import OrderTypes from ..utils import ( calculate_unmatched_exposure, @@ -122,7 +125,7 @@ def live_orders(self) -> Iterable[BaseOrder]: def has_live_orders(self) -> bool: return bool(self._live_orders) - def process_closed_market(self, market, market_book) -> None: + def process_closed_market(self, market: Market, market_book: MarketBook) -> None: number_of_winners = len( [runner for runner in market_book.runners if runner.status == "WINNER"] ) @@ -163,7 +166,7 @@ def process_cleared_orders(self, cleared_orders) -> list[BaseOrder]: """ position """ - def market_exposure(self, strategy, market_book) -> float: + def market_exposure(self, strategy: BaseStrategy, market_book: MarketBook) -> float: """Returns worst-case exposure for market, which is the maximum potential loss (negative), arising from the worst race outcome, or the minimum potential profit (positive). """ @@ -182,7 +185,7 @@ def market_exposure(self, strategy, market_book) -> float: worst_differences = sorted(differences)[: market_book.number_of_winners] return sum(worst_possible_profits_on_loses) + sum(worst_differences) - def selection_exposure(self, strategy, lookup: tuple) -> float: + def selection_exposure(self, strategy: BaseStrategy, lookup: tuple) -> float: """Returns strategy/selection exposure, which is the worse-case loss arising from the selection either winning or losing. Can be positive or zero. positive = potential loss @@ -195,7 +198,9 @@ def selection_exposure(self, strategy, lookup: tuple) -> float: ) return max(exposure, 0.0) - def get_exposures(self, strategy, lookup: tuple, exclusion=None) -> dict: + def get_exposures( + self, strategy: BaseStrategy, lookup: tuple, exclusion=None + ) -> dict: """Returns strategy/selection exposures as a dict.""" mb, ml = [], [] # matched bets, (price, size) ub, ul = [], [] # unmatched bets, (price, size) @@ -259,7 +264,7 @@ def get_exposures(self, strategy, lookup: tuple, exclusion=None) -> dict: """ getters / setters """ - def complete_order(self, order) -> None: + def complete_order(self, order: BaseOrder) -> None: self._live_orders.remove(order) def has_order(self, customer_order_ref: str) -> bool: @@ -270,7 +275,7 @@ def has_trade(self, trade_id: str) -> bool: __contains__ = has_order - def __setitem__(self, customer_order_ref: str, order) -> None: + def __setitem__(self, customer_order_ref: str, order: BaseOrder) -> None: self.active = True self._orders[customer_order_ref] = order self._bet_id_lookup[order.bet_id] = order From dcb8e49686531af000d33ce3b8c314214a009894 Mon Sep 17 00:00:00 2001 From: Maurice Berk Date: Fri, 24 Jan 2025 11:46:52 +0000 Subject: [PATCH 4/7] Add type hints to order.py --- flumine/order/order.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/flumine/order/order.py b/flumine/order/order.py index f53e7855..bb78243a 100644 --- a/flumine/order/order.py +++ b/flumine/order/order.py @@ -5,12 +5,13 @@ import string import collections from enum import Enum -from typing import Union, Optional +from typing import Any, Optional, Union import betdaq.filters from betfairlightweight.resources.bettingresources import CurrentOrder from ..clients.clients import ExchangeType +from ..order.trade import Trade from .ordertype import LimitOrder, LimitOnCloseOrder, MarketOnCloseOrder, OrderTypes from .responses import Responses from ..exceptions import OrderUpdateError @@ -59,7 +60,7 @@ class BaseOrder: def __init__( self, - trade, + trade: Trade, side: str, order_type: Union[LimitOrder, LimitOnCloseOrder, MarketOnCloseOrder], handicap: float = 0, @@ -284,7 +285,7 @@ def notes_str(self) -> str: return ",".join(str(x) for x in self.notes.values()) @property - def info(self) -> dict: + def info(self) -> dict[str, Any]: return { "market_id": self.market_id, "selection_id": self.selection_id, @@ -421,19 +422,19 @@ def create_place_instruction(self) -> dict: "handicap": self.handicap, } - def create_cancel_instruction(self) -> dict: + def create_cancel_instruction(self) -> dict[str, Any]: return { "betId": self.bet_id, "sizeReduction": self.update_data.get("size_reduction"), } - def create_update_instruction(self) -> dict: + def create_update_instruction(self) -> dict[str, Any]: return { "betId": self.bet_id, "newPersistenceType": self.order_type.persistence_type, } - def create_replace_instruction(self) -> dict: + def create_replace_instruction(self) -> dict[str, Any]: return {"betId": self.bet_id, "newPrice": self.update_data["new_price"]} # currentOrder From e925cc059eca5df6412fce7e792bae2975ecbe31 Mon Sep 17 00:00:00 2001 From: Maurice Berk Date: Fri, 24 Jan 2025 11:49:35 +0000 Subject: [PATCH 5/7] Finish typing blotter.py --- flumine/markets/blotter.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flumine/markets/blotter.py b/flumine/markets/blotter.py index f1bda90e..5146b6f8 100644 --- a/flumine/markets/blotter.py +++ b/flumine/markets/blotter.py @@ -4,6 +4,7 @@ from betfairlightweight.resources.bettingresources import MarketBook +from ..clients.baseclient import BaseClient from ..markets.market import Market from ..order.ordertype import OrderTypes from ..utils import ( @@ -92,7 +93,7 @@ def strategy_selection_orders( def client_orders( self, - client, + client: BaseClient, order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, ) -> list[BaseOrder]: @@ -105,7 +106,7 @@ def client_orders( def client_strategy_orders( self, - client, + client: BaseClient, strategy: BaseStrategy, order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, From e984bb96ac48b287fc6e122c114e13375ff2b4a1 Mon Sep 17 00:00:00 2001 From: Maurice Berk Date: Thu, 27 Feb 2025 06:45:45 +0000 Subject: [PATCH 6/7] Type markets.py --- flumine/markets/markets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flumine/markets/markets.py b/flumine/markets/markets.py index 652d8ea9..51270186 100644 --- a/flumine/markets/markets.py +++ b/flumine/markets/markets.py @@ -52,11 +52,11 @@ def get_order_from_bet_id( return blotter.get_order_bet_id(bet_id) @property - def markets(self) -> dict: + def markets(self) -> dict[str, Market]: return self._markets @property - def open_market_ids(self) -> list: + def open_market_ids(self) -> list[str]: return [m.market_id for m in self if m.status == "OPEN"] @property From 65b0611061bcc8037e2f54a6edc1ed66ab1f897a Mon Sep 17 00:00:00 2001 From: Maurice Berk Date: Thu, 27 Feb 2025 07:09:17 +0000 Subject: [PATCH 7/7] Use TYPE_CHECKING --- flumine/markets/blotter.py | 34 ++++++++++++++++++++-------------- flumine/order/order.py | 8 +++++--- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/flumine/markets/blotter.py b/flumine/markets/blotter.py index 5146b6f8..9f389728 100644 --- a/flumine/markets/blotter.py +++ b/flumine/markets/blotter.py @@ -1,11 +1,8 @@ import logging from typing import Iterable, Optional, List from collections import defaultdict +from typing import TYPE_CHECKING -from betfairlightweight.resources.bettingresources import MarketBook - -from ..clients.baseclient import BaseClient -from ..markets.market import Market from ..order.ordertype import OrderTypes from ..utils import ( calculate_unmatched_exposure, @@ -13,7 +10,12 @@ STRATEGY_NAME_HASH_LENGTH, ) from ..order.order import BaseOrder, OrderStatus -from ..strategy.strategy import BaseStrategy + +if TYPE_CHECKING: + from betfairlightweight.resources.bettingresources import MarketBook + from ..clients.baseclient import BaseClient + from ..markets.market import Market + from ..strategy.strategy import BaseStrategy logger = logging.getLogger(__name__) @@ -63,7 +65,7 @@ def get_order_bet_id(self, bet_id: str) -> Optional[BaseOrder]: def strategy_orders( self, - strategy: BaseStrategy, + strategy: "BaseStrategy", order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, ) -> list[BaseOrder]: @@ -77,7 +79,7 @@ def strategy_orders( def strategy_selection_orders( self, - strategy: BaseStrategy, + strategy: "BaseStrategy", selection_id: int, handicap: float = 0, order_status: Optional[List[OrderStatus]] = None, @@ -93,7 +95,7 @@ def strategy_selection_orders( def client_orders( self, - client: BaseClient, + client: "BaseClient", order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, ) -> list[BaseOrder]: @@ -106,8 +108,8 @@ def client_orders( def client_strategy_orders( self, - client: BaseClient, - strategy: BaseStrategy, + client: "BaseClient", + strategy: "BaseStrategy", order_status: Optional[List[OrderStatus]] = None, matched_only: Optional[bool] = None, ) -> list[BaseOrder]: @@ -126,7 +128,9 @@ def live_orders(self) -> Iterable[BaseOrder]: def has_live_orders(self) -> bool: return bool(self._live_orders) - def process_closed_market(self, market: Market, market_book: MarketBook) -> None: + def process_closed_market( + self, market: "Market", market_book: "MarketBook" + ) -> None: number_of_winners = len( [runner for runner in market_book.runners if runner.status == "WINNER"] ) @@ -167,7 +171,9 @@ def process_cleared_orders(self, cleared_orders) -> list[BaseOrder]: """ position """ - def market_exposure(self, strategy: BaseStrategy, market_book: MarketBook) -> float: + def market_exposure( + self, strategy: "BaseStrategy", market_book: "MarketBook" + ) -> float: """Returns worst-case exposure for market, which is the maximum potential loss (negative), arising from the worst race outcome, or the minimum potential profit (positive). """ @@ -186,7 +192,7 @@ def market_exposure(self, strategy: BaseStrategy, market_book: MarketBook) -> fl worst_differences = sorted(differences)[: market_book.number_of_winners] return sum(worst_possible_profits_on_loses) + sum(worst_differences) - def selection_exposure(self, strategy: BaseStrategy, lookup: tuple) -> float: + def selection_exposure(self, strategy: "BaseStrategy", lookup: tuple) -> float: """Returns strategy/selection exposure, which is the worse-case loss arising from the selection either winning or losing. Can be positive or zero. positive = potential loss @@ -200,7 +206,7 @@ def selection_exposure(self, strategy: BaseStrategy, lookup: tuple) -> float: return max(exposure, 0.0) def get_exposures( - self, strategy: BaseStrategy, lookup: tuple, exclusion=None + self, strategy: "BaseStrategy", lookup: tuple, exclusion=None ) -> dict: """Returns strategy/selection exposures as a dict.""" mb, ml = [], [] # matched bets, (price, size) diff --git a/flumine/order/order.py b/flumine/order/order.py index bb78243a..e11d303e 100644 --- a/flumine/order/order.py +++ b/flumine/order/order.py @@ -5,19 +5,21 @@ import string import collections from enum import Enum -from typing import Any, Optional, Union +from typing import Any, Optional, TYPE_CHECKING, Union import betdaq.filters from betfairlightweight.resources.bettingresources import CurrentOrder from ..clients.clients import ExchangeType -from ..order.trade import Trade from .ordertype import LimitOrder, LimitOnCloseOrder, MarketOnCloseOrder, OrderTypes from .responses import Responses from ..exceptions import OrderUpdateError from ..simulation.simulatedorder import SimulatedOrder from .. import config +if TYPE_CHECKING: + from ..order.trade import Trade + logger = logging.getLogger(__name__) @@ -60,7 +62,7 @@ class BaseOrder: def __init__( self, - trade: Trade, + trade: "Trade", side: str, order_type: Union[LimitOrder, LimitOnCloseOrder, MarketOnCloseOrder], handicap: float = 0,