Skip to content

Commit

Permalink
Add get_race_change_from_race_file
Browse files Browse the repository at this point in the history
  • Loading branch information
mberk committed Dec 18, 2022
1 parent 206b05c commit 04aa5b9
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 11 deletions.
61 changes: 50 additions & 11 deletions betfairutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2353,6 +2353,25 @@ def create_market_book_generator_from_prices_file(
return stream.listener.snap()


def create_race_change_generator_from_race_file(
path_to_race_file: Union[str, Path],
) -> Generator[Dict[str, Any], None, None]:
import smart_open
from unittest.mock import patch

trading = APIClient(username="", password="", app_key="")
stream = trading.streaming.create_historical_generator_stream(
file_path=path_to_race_file,
listener=StreamListener(max_latency=None, lightweight=True, update_clk=False),
operation="raceSubscription",
)
with patch("builtins.open", smart_open.open):
g = stream.get_generator()
for rcs in g():
for rc in rcs:
yield rc


def read_prices_file(
path_to_prices_file: Union[str, Path],
lightweight: bool = True,
Expand Down Expand Up @@ -2411,18 +2430,38 @@ def read_prices_file(


def read_race_file(path_to_race_file: Union[str, Path]) -> List[Dict[str, Any]]:
import smart_open
from unittest.mock import patch
race_changes = list(create_race_change_generator_from_race_file(path_to_race_file))
return race_changes

trading = APIClient(username="", password="", app_key="")
stream = trading.streaming.create_historical_generator_stream(
file_path=path_to_race_file,
listener=StreamListener(max_latency=None, lightweight=True, update_clk=False),
operation="raceSubscription",
)
with patch("builtins.open", smart_open.open):
g = stream.get_generator()
return [rc for rcs in g() for rc in rcs]

def get_race_change_from_race_file(
path_to_race_file: Union[str, Path],
gate_name: Optional[str] = None,
publish_time: Optional[int] = None,
) -> Optional[Dict[str, Any]]:
"""
Search a recorded race file for the first update after the given criterion. You can search EITHER by the gate name,
for example "1f" to get the first race change after entering the final furlong, OR by publish time. The latter is
useful when cross-referencing with the Betfair price stream. This function is memory efficient in that only a single
race change is held in memory at a time
:param path_to_race_file: The path to the recorded race file to search
:param gate_name: The Gmax gate name to search for, for example "1f"
:param publish_time: The Betfair publish time to search for
:return: The first race change meeting the search criterion
:raises: AssertionError unless exactly one of gate_name and publish_time is not None
"""
assert not (gate_name is not None and publish_time is not None)

g = create_race_change_generator_from_race_file(path_to_race_file)
if gate_name is not None:
for race_change in g:
if (race_change.get("rpc") or {}).get("g") == gate_name:
return race_change
else:
for race_change in g:
if race_change["pt"] >= publish_time:
return race_change


def remove_bet_from_runner_book(
Expand Down
58 changes: 58 additions & 0 deletions tests/test_non_prices.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from betfairutil import get_event_id_from_string
from betfairutil import get_market_id_from_string
from betfairutil import get_pre_event_volume_traded_from_prices_file
from betfairutil import get_race_change_from_race_file
from betfairutil import get_race_id_from_string
from betfairutil import get_runner_book_from_market_book
from betfairutil import get_selection_id_to_runner_name_map_from_market_catalogue
Expand Down Expand Up @@ -145,6 +146,24 @@ def market_book(market_definition: Dict[str, Any]):
}


@pytest.fixture
def race_change():
return {
"id": "31945198.2354",
"mid": "1.207120593",
"rpc": {
"ft": 1670024521800,
"g": "",
"st": 0,
"rt": 0,
"spd": 0,
"prg": 1106.4,
"ord": [],
"J": [],
},
}


def test_side():
assert Side.LAY.other_side == Side.BACK

Expand Down Expand Up @@ -773,3 +792,42 @@ def test_get_winners_from_market_definition(market_definition: Dict[str, Any]):
assert get_winners_from_market_definition(market_definition) == [
market_definition["runners"][0]["id"]
]


def test_get_race_change_from_race_file(race_change: Dict[str, Any], tmp_path: Path):
path_to_race_file = tmp_path / f"31945198.2354.jsonl.gz"
with smart_open.open(path_to_race_file, "w") as f:
f.write(
json.dumps(
{
"op": "rcm",
"clk": 0,
"pt": race_change["rpc"]["ft"],
"rc": [race_change],
}
)
)
f.write("\n")
with pytest.raises(AssertionError):
get_race_change_from_race_file(
path_to_race_file, gate_name="1f", publish_time=race_change["rpc"]["ft"]
)

assert get_race_change_from_race_file(path_to_race_file, gate_name="1f") is None
assert (
get_race_change_from_race_file(
path_to_race_file, publish_time=race_change["rpc"]["ft"] + 1
)
is None
)

assert (
get_race_change_from_race_file(path_to_race_file, gate_name="")["rpc"]
== race_change["rpc"]
)
assert (
get_race_change_from_race_file(
path_to_race_file, publish_time=race_change["rpc"]["ft"]
)["rpc"]
== race_change["rpc"]
)

0 comments on commit 04aa5b9

Please sign in to comment.