diff --git a/bfxapi/tests/helpers.py b/bfxapi/tests/helpers.py index 1670beb..e1b5fee 100644 --- a/bfxapi/tests/helpers.py +++ b/bfxapi/tests/helpers.py @@ -5,7 +5,7 @@ import asyncio from .. import Client, BfxWebsocket def get_now(): - return int(round(time.time() * 1000)) + return int(round(time.time() * 1000)) class StubbedWebsocket(BfxWebsocket): def __new__(cls, *args, **kwargs): diff --git a/bfxapi/tests/test_ws_orders.py b/bfxapi/tests/test_ws_orders.py index d981721..39067e8 100644 --- a/bfxapi/tests/test_ws_orders.py +++ b/bfxapi/tests/test_ws_orders.py @@ -110,3 +110,99 @@ async def test_events_on_cancel_order(): assert close_res.price == 10 assert close_res.type == 'EXCHANGE LIMIT' +@pytest.mark.asyncio +async def test_closed_callback_on_submit_order_closed(): + client = create_stubbed_client() + # publsh connection created message + await ws_publish_connection_init(client.ws) + ## send auth accepted + await ws_publish_auth_accepted(client.ws) + async def c(order): + client.ws._emit('c1', order) + callback_wait = EventWatcher.watch(client.ws, 'c1') + # override cid generation + client.ws.orderManager._gen_unqiue_cid = lambda: 123 + await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onClose=c) + await client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + callback_wait.wait_until_complete() + + +@pytest.mark.asyncio +async def test_confirmed_callback_on_submit_order_closed(): + client = create_stubbed_client() + # publsh connection created message + await ws_publish_connection_init(client.ws) + ## send auth accepted + await ws_publish_auth_accepted(client.ws) + async def c(order): + client.ws._emit('c1', order) + callback_wait = EventWatcher.watch(client.ws, 'c1') + # override cid generation + client.ws.orderManager._gen_unqiue_cid = lambda: 123 + await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) + await client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + callback_wait.wait_until_complete() + +@pytest.mark.asyncio +async def test_confirmed_callback_on_submit_new_order(): + client = create_stubbed_client() + # publsh connection created message + await ws_publish_connection_init(client.ws) + ## send auth accepted + await ws_publish_auth_accepted(client.ws) + async def c(order): + client.ws._emit('c1', order) + callback_wait = EventWatcher.watch(client.ws, 'c1') + # override cid generation + client.ws.orderManager._gen_unqiue_cid = lambda: 123 + await client.ws.submit_order('tBTCUSD', 19000, 0.01, 'EXCHANGE MARKET', onConfirm=c) + await client.ws.publish([0,"on",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262833410,-1,-1,"EXCHANGE LIMIT",None,None,None,0,"ACTIVE",None,None,15980,0,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + callback_wait.wait_until_complete() + +@pytest.mark.asyncio +async def test_confirmed_callback_on_submit_order_update(): + client = create_stubbed_client() + # publsh connection created message + await ws_publish_connection_init(client.ws) + ## send auth accepted + await ws_publish_auth_accepted(client.ws) + async def c(order): + client.ws._emit('c1', order) + callback_wait = EventWatcher.watch(client.ws, 'c1') + # override cid generation + client.ws.orderManager._gen_unqiue_cid = lambda: 123 + await client.ws.update_order(123, price=100, onConfirm=c) + await client.ws.publish([0,"ou",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262846964,-0.5,-1,"EXCHANGE LIMIT",None,None,None,0,"PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + callback_wait.wait_until_complete() + +@pytest.mark.asyncio +async def test_confirmed_callback_on_submit_cancel_order(): + client = create_stubbed_client() + # publsh connection created message + await ws_publish_connection_init(client.ws) + ## send auth accepted + await ws_publish_auth_accepted(client.ws) + async def c(order): + client.ws._emit('c1', order) + callback_wait = EventWatcher.watch(client.ws, 'c1') + # override cid generation + client.ws.orderManager._gen_unqiue_cid = lambda: 123 + await client.ws.cancel_order(123, onConfirm=c) + await client.ws.publish([0,"oc",[123,None,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + callback_wait.wait_until_complete() + +@pytest.mark.asyncio +async def test_confirmed_callback_on_submit_cancel_group_order(): + client = create_stubbed_client() + # publsh connection created message + await ws_publish_connection_init(client.ws) + ## send auth accepted + await ws_publish_auth_accepted(client.ws) + async def c(order): + client.ws._emit('c1', order) + callback_wait = EventWatcher.watch(client.ws, 'c1') + # override cid generation + client.ws.orderManager._gen_unqiue_cid = lambda: 123 + await client.ws.cancel_order_group(123, onConfirm=c) + await client.ws.publish([0,"oc",[1548262833910,123,1548262833910,"tBTCUSD",1548262833379,1548262888016,0,-1,"EXCHANGE LIMIT",None,None,None,0,"EXECUTED @ 15980.0(-0.5): was PARTIALLY FILLED @ 15980.0(-0.5)",None,None,15980,15980,0,0,None,None,None,0,0,None,None,None,"API>BFX",None,None,None]]) + callback_wait.wait_until_complete() diff --git a/bfxapi/utils/testing_tools.py b/bfxapi/utils/testing_tools.py new file mode 100644 index 0000000..143df5d --- /dev/null +++ b/bfxapi/utils/testing_tools.py @@ -0,0 +1,90 @@ +import time +import json +import asyncio + +from .. import Client, BfxWebsocket + +def get_now(): + return int(round(time.time() * 1000)) + +class StubbedWebsocket(BfxWebsocket): + def __new__(cls, *args, **kwargs): + instance = super(StubbedWebsocket, cls).__new__(cls, *args, **kwargs) + instance.sent_items = [] + instance.published_items = [] + return instance + + async def _main(self, host): + print ("Faking wesocket connection to {}".format(host)) + + def get_ws(self): + return self + + async def publish(self, data, is_json=True): + self.published_items += [{ + 'time': get_now(), + 'data': data + }] + # convert to string and push through the websocket + data = json.dumps(data) if is_json else data + return await self.on_message(data) + + async def publish_auth_confirmation(self): + return self.publish({"event":"auth","status":"OK","chanId":0,"userId":269499,"auth_id":"58aa0472-b1a9-4690-8ab8-300d68e66aaf","caps":{"orders":{"read":1,"write":1},"account":{"read":1,"write":0},"funding":{"read":1,"write":1},"history":{"read":1,"write":0},"wallets":{"read":1,"write":1},"withdraw":{"read":0,"write":1},"positions":{"read":1,"write":1}}}) + + async def send(self, data_string): + self.sent_items += [{ + 'time': get_now(), + 'data': data_string + }] + + def get_published_items(self): + return self.published_items + + def get_sent_items(self): + return self.sent_items + + def get_last_sent_item(self): + return self.sent_items[-1:][0] + + def get_sent_items_count(self): + return len(self.sent_items) + +class EventWatcher(): + + def __init__(self, ws, event): + self.value = None + self.event = event + ws.once(event, self._finish) + + def _finish(self, value): + self.value = value or {} + + @classmethod + def watch(cls, ws, event): + return EventWatcher(ws, event) + + def wait_until_complete(self, max_wait_time=5): + counter = 0 + while self.value == None: + if counter > 5: + raise Exception('Wait time limit exceeded for event {}'.format(self.event)) + time.sleep(1) + counter += 1 + return self.value + +def create_stubbed_client(*args, **kwargs): + client = Client(*args, **kwargs) + # no support for rest stubbing yet + client.rest = None + client.ws = StubbedWebsocket(*args, **kwargs) + return client + +async def ws_publish_auth_accepted(ws): + return await ws.publish({"event":"auth","status":"OK","chanId":0,"userId":269499,"auth_id":"58aa0472-b1a9-4690-8ab8-300d68e66aaf","caps":{"orders":{"read":1,"write":1},"account":{"read":1,"write":0},"funding":{"read":1,"write":1},"history":{"read":1,"write":0},"wallets":{"read":1,"write":1},"withdraw":{"read":0,"write":1},"positions":{"read":1,"write":1}}}) + +async def ws_publish_connection_init(ws): + return await ws.publish({"event":"info","version":2,"serverId":"748c00f2-250b-46bb-8519-ce1d7d68e4f0","platform":{"status":1}}) + +async def ws_publish_conf_accepted(ws, flags_code): + return await ws.publish({"event":"conf","status":"OK","flags":flags_code})