async def test_account_statement(self): with patch.object(BetfairClient, "request", return_value=BetfairResponses.account_details()): detail = await self.client.get_account_details() with patch.object( BetfairClient, "request", return_value=BetfairResponses.account_funds_no_exposure()): funds = await self.client.get_account_funds() result = betfair_account_to_account_state( account_detail=detail, account_funds=funds, event_id=self.uuid, ts_event=0, ts_init=0, ) expected = AccountState( account_id=AccountId(issuer="BETFAIR", number="Testy-McTest"), account_type=AccountType.CASH, base_currency=GBP, reported=True, # reported balances=[ AccountBalance(GBP, Money(1000.0, GBP), Money(0.00, GBP), Money(1000.0, GBP)) ], info={ "funds": funds, "detail": detail }, event_id=self.uuid, ts_event=result.ts_event, ts_init=result.ts_init, ) assert result == expected
async def test_exception_handling(self): with mock_client_request( response=BetfairResponses.account_funds_error()): with pytest.raises(BetfairAPIError) as ex: await self.client.get_account_funds(wallet="not a real walltet" ) assert ex.value.message == "DSC-0018"
def instrument_list(mock_load_markets_metadata, loop: asyncio.AbstractEventLoop): """Prefill `INSTRUMENTS` cache for tests""" global INSTRUMENTS # Setup logger = LiveLogger(loop=loop, clock=LiveClock(), level_stdout=LogLevel.ERROR) client = BetfairTestStubs.betfair_client(loop=loop, logger=logger) logger = LiveLogger(loop=loop, clock=LiveClock(), level_stdout=LogLevel.DEBUG) instrument_provider = BetfairInstrumentProvider(client=client, logger=logger, market_filter={}) # Load instruments market_ids = BetfairDataProvider.market_ids() catalog = { r["marketId"]: r for r in BetfairResponses.betting_list_market_catalogue()["result"] if r["marketId"] in market_ids } mock_load_markets_metadata.return_value = catalog t = loop.create_task( instrument_provider.load_all_async( market_filter={"market_id": market_ids})) loop.run_until_complete(t) # Fill INSTRUMENTS global cache INSTRUMENTS.extend(instrument_provider.list_all()) assert INSTRUMENTS
async def test_get_account_details(self): with mock_client_request( response=BetfairResponses.account_details()) as mock_request: account = await self.client.get_account_details() assert account["pointsBalance"] == 10 result = mock_request.call_args.kwargs expected = BetfairRequests.account_details() assert result == expected
async def test_connect(self): self.client.session_token = None with mock_client_request( response=BetfairResponses.cert_login()) as mock_request: await self.client.connect() assert self.client.session_token result = mock_request.call_args.kwargs expected = BetfairRequests.cert_login() assert result == expected
async def test_betfair_order_reduces_balance(self): # Arrange self.client.stream = MagicMock() self.exec_engine.start() await asyncio.sleep(1) balance = self.cache.account_for_venue(self.venue).balances()[GBP] order = BetfairTestStubs.make_order(price=Price.from_str("0.5"), quantity=Quantity.from_int(10)) self.cache.add_order(order=order, position_id=None) mock_betfair_request(self.betfair_client, BetfairResponses.betting_place_order_success()) command = BetfairTestStubs.submit_order_command(order=order) self.client.submit_order(command) await asyncio.sleep(0.01) # Act balance_order = self.cache.account_for_venue( BETFAIR_VENUE).balances()[GBP] # Cancel the order, balance should return command = BetfairTestStubs.cancel_order_command( client_order_id=order.client_order_id, venue_order_id=order.venue_order_id) mock_betfair_request(self.betfair_client, BetfairResponses.betting_cancel_orders_success()) self.client.cancel_order(command) await asyncio.sleep(0.1) balance_cancel = self.cache.account_for_venue( BETFAIR_VENUE).balances()[GBP] # Assert assert balance.free == Money(1000.0, GBP) assert balance_order.free == Money(990.0, GBP) assert balance_cancel.free == Money(1000.0, GBP) self.exec_engine.kill() await asyncio.sleep(1)
async def test_submit_order_error(self): # Arrange command = BetfairTestStubs.submit_order_command() mock_betfair_request(self.betfair_client, BetfairResponses.betting_place_order_error()) # Act self.client.submit_order(command) await asyncio.sleep(0) # Assert submitted, rejected = self.messages assert isinstance(submitted, OrderSubmitted) assert isinstance(rejected, OrderRejected) assert rejected.reason == "PERMISSION_DENIED: ERROR_IN_ORDER"
async def test_submit_order_success(self): # Arrange command = BetfairTestStubs.submit_order_command() mock_betfair_request(self.betfair_client, BetfairResponses.betting_place_order_success()) # Act self.client.submit_order(command) await asyncio.sleep(0) # Assert submitted, accepted = self.messages assert isinstance(submitted, OrderSubmitted) assert isinstance(accepted, OrderAccepted) assert accepted.venue_order_id == VenueOrderId("228302937743")
async def test_list_market_catalogue(self): market_filter = { "eventTypeIds": ["7"], "marketBettingTypes": ["ODDS"], } with mock_client_request( response=BetfairResponses.betting_list_market_catalogue( )) as mock_request: catalogue = await self.client.list_market_catalogue( filter_=market_filter) assert catalogue result = mock_request.call_args.kwargs expected = BetfairRequests.betting_list_market_catalogue() assert result == expected
async def test_make_instruments(self): # Arrange list_market_catalogue_data = { m["marketId"]: m for m in BetfairResponses.betting_list_market_catalogue()["result"] if m["eventType"]["name"] == "Basketball" } # Act instruments = [ instrument for metadata in list_market_catalogue_data.values() for instrument in make_instruments(metadata, currency="GBP") ] # Assert assert len(instruments) == 30412
async def test_list_cleared_orders(self): with mock_client_request( response=BetfairResponses.list_cleared_orders()) as req: cleared_orders = await self.client.list_cleared_orders() assert len(cleared_orders) == 14 expected = { "id": 1, "jsonrpc": "2.0", "method": "SportsAPING/v1.0/listClearedOrders", "params": { "betStatus": "SETTLED", "fromRecord": 0 }, } result = req.call_args.kwargs["json"] assert result == expected
async def test_list_current_orders(self): with mock_client_request( response=BetfairResponses.list_current_orders()) as req: current_orders = await self.client.list_current_orders() assert len(current_orders) == 4 expected = { "id": 1, "jsonrpc": "2.0", "method": "SportsAPING/v1.0/listCurrentOrders", "params": { "fromRecord": 0, "orderBy": "BY_PLACE_TIME" }, } result = req.call_args.kwargs["json"] assert result == expected
async def test_cancel_order_fail(self): # Arrange order = BetfairTestStubs.make_submitted_order() self.cache.add_order(order, position_id=TestIdStubs.position_id()) command = BetfairTestStubs.cancel_order_command( instrument_id=order.instrument_id, client_order_id=order.client_order_id, venue_order_id=VenueOrderId("228302937743"), ) mock_betfair_request(self.betfair_client, BetfairResponses.betting_cancel_orders_error()) # Act self.client.cancel_order(command) await asyncio.sleep(0) # Assert pending_cancel, cancelled = self.messages assert isinstance(pending_cancel, OrderPendingCancel) assert isinstance(cancelled, OrderCancelRejected)
async def test_modify_order_error_no_venue_id(self): # Arrange order = BetfairTestStubs.make_submitted_order() self.cache.add_order(order, position_id=TestIdStubs.position_id()) command = BetfairTestStubs.modify_order_command( instrument_id=order.instrument_id, client_order_id=order.client_order_id, venue_order_id="", ) mock_betfair_request(self.betfair_client, BetfairResponses.betting_replace_orders_success()) # Act self.client.modify_order(command) await asyncio.sleep(0) # Assert pending_update, rejected = self.messages assert isinstance(pending_update, OrderPendingUpdate) assert isinstance(rejected, OrderModifyRejected) assert rejected.reason == "ORDER MISSING VENUE_ORDER_ID"
async def test_replace_orders_multi(self): instrument = BetfairTestStubs.betting_instrument() update_order_command = BetfairTestStubs.modify_order_command( instrument_id=instrument.id, client_order_id=ClientOrderId("1628717246480-1.186260932-rpl-0"), ) replace_order = order_update_to_betfair( command=update_order_command, venue_order_id=VenueOrderId("240718603398"), side=OrderSide.BUY, instrument=instrument, ) with mock_client_request( response=BetfairResponses.betting_replace_orders_success_multi( )) as req: resp = await self.client.replace_orders(**replace_order) assert len(resp["oc"][0]["orc"][0]["uo"]) == 2 expected = BetfairRequests.betting_replace_order() result = req.call_args.kwargs["json"] assert result == expected
async def test_modify_order_error_order_doesnt_exist(self): # Arrange venue_order_id = VenueOrderId("229435133092") order = BetfairTestStubs.make_accepted_order( venue_order_id=venue_order_id) command = BetfairTestStubs.modify_order_command( instrument_id=order.instrument_id, client_order_id=order.client_order_id, venue_order_id=venue_order_id, ) mock_betfair_request(self.betfair_client, BetfairResponses.betting_replace_orders_success()) # Act self.client.modify_order(command) await asyncio.sleep(0) # Assert pending_update, rejected = self.messages assert isinstance(pending_update, OrderPendingUpdate) assert isinstance(rejected, OrderModifyRejected) assert rejected.reason == "ORDER NOT IN CACHE"
async def test_modify_order_success(self): # Arrange venue_order_id = VenueOrderId("240808576108") order = BetfairTestStubs.make_accepted_order( venue_order_id=venue_order_id) command = BetfairTestStubs.modify_order_command( instrument_id=order.instrument_id, client_order_id=order.client_order_id, venue_order_id=venue_order_id, ) mock_betfair_request(self.betfair_client, BetfairResponses.betting_replace_orders_success()) # Act self.cache.add_order(order, PositionId("1")) self.client.modify_order(command) await asyncio.sleep(0) # Assert pending_update, updated = self.messages assert isinstance(pending_update, OrderPendingUpdate) assert isinstance(updated, OrderUpdated) assert updated.price == Price.from_str("0.02000")