From c82826174074ff96a958f98c895801a50b13848d Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 30 Sep 2021 12:10:01 -0400 Subject: [PATCH 1/7] Activate/focus original window after feed reset --- snippets/ib_data_reset.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/snippets/ib_data_reset.py b/snippets/ib_data_reset.py index f5d4ca39..d2a5c63d 100644 --- a/snippets/ib_data_reset.py +++ b/snippets/ib_data_reset.py @@ -25,6 +25,8 @@ import i3ipc i3 = i3ipc.Connection() t = i3.get_tree() +orig_win_id = t.find_focused().window + # for tws win_names: list[str] = [ 'Interactive Brokers', # tws running in i3 @@ -59,3 +61,10 @@ for name in win_names: # hackzorzes 'key', 'ctrl+alt+f', ]) + +# re-activate and focus original window +subprocess.call([ + 'xdotool', + 'windowactivate', '--sync', str(orig_win_id), + 'click', '--window', str(orig_win_id), '1', +]) From 0bcaeda784309cdec82fb34ea57956ec602d59bb Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Fri, 22 Oct 2021 12:07:04 -0400 Subject: [PATCH 2/7] Repeat the click 3 times --- snippets/ib_data_reset.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/snippets/ib_data_reset.py b/snippets/ib_data_reset.py index d2a5c63d..a65321dc 100644 --- a/snippets/ib_data_reset.py +++ b/snippets/ib_data_reset.py @@ -53,14 +53,16 @@ for name in win_names: # move mouse to bottom left of window (where there should # be nothing to click). - 'mousemove_relative', '--sync', str(w-3), str(h-3), + 'mousemove_relative', '--sync', str(w-4), str(h-4), # NOTE: we may need to stick a `--retry 3` in here.. - 'click', '--window', win_id, '1', + 'click', '--window', win_id, '--repeat', '3', '1', # hackzorzes 'key', 'ctrl+alt+f', - ]) + ], + timeout=1, + ) # re-activate and focus original window subprocess.call([ From 0e4a7e38469b9ccedb375952e4b69e745c22ae81 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Fri, 19 Nov 2021 10:29:04 -0500 Subject: [PATCH 3/7] Only warn on slow quote query --- piker/brokers/ib.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/piker/brokers/ib.py b/piker/brokers/ib.py index 248039d4..d9e22d51 100644 --- a/piker/brokers/ib.py +++ b/piker/brokers/ib.py @@ -505,8 +505,14 @@ class Client: ) # ensure a last price gets filled in before we deliver quote - while isnan(ticker.last): - ticker = await ticker.updateEvent + for _ in range(25): + if isnan(ticker.last): + ticker = await ticker.updateEvent + await asyncio.sleep(0.2) + else: + log.warning( + f'Symbol {symbol} is not returning a quote ' + 'it may be outside trading hours?') details = (await details_fute)[0] return contract, ticker, details @@ -1350,10 +1356,11 @@ async def stream_quotes( # TODO: support multiple subscriptions sym = symbols[0] - contract, first_ticker, details = await _trio_run_client_method( - method='get_quote', - symbol=sym, - ) + with trio.fail_after(3): + contract, first_ticker, details = await _trio_run_client_method( + method='get_quote', + symbol=sym, + ) # stream = await start_aio_quote_stream(symbol=sym, contract=contract) async with open_aio_quote_stream( From d69d3b319eec9d548ffaf266752f39378d292e31 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 2 Dec 2021 08:08:37 -0500 Subject: [PATCH 4/7] Lengthen startup quote get timeout --- piker/brokers/ib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/piker/brokers/ib.py b/piker/brokers/ib.py index d9e22d51..77bf513e 100644 --- a/piker/brokers/ib.py +++ b/piker/brokers/ib.py @@ -1164,6 +1164,7 @@ async def backfill_bars( """ out, fails = await get_bars(sym) + if out is None: raise RuntimeError("Could not pull currrent history?!") @@ -1356,7 +1357,7 @@ async def stream_quotes( # TODO: support multiple subscriptions sym = symbols[0] - with trio.fail_after(3): + with trio.fail_after(5): contract, first_ticker, details = await _trio_run_client_method( method='get_quote', symbol=sym, From f1d61ac01b9e1c8dcd6aa5efeb91537b31ceac2b Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Thu, 9 Dec 2021 08:22:22 -0500 Subject: [PATCH 5/7] WIP ib: toying with showing history before first quote --- piker/brokers/ib.py | 64 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/piker/brokers/ib.py b/piker/brokers/ib.py index 77bf513e..cb4e585a 100644 --- a/piker/brokers/ib.py +++ b/piker/brokers/ib.py @@ -489,20 +489,30 @@ class Client: formatDate=2, # timezone aware UTC datetime ) - async def get_quote( + async def get_sym_details( self, symbol: str, - ) -> Ticker: - """Return a single quote for symbol. + ) -> tuple[Contract, Ticker, ContractDetails]: - """ contract = await self.find_contract(symbol) - - details_fute = self.ib.reqContractDetailsAsync(contract) ticker: Ticker = self.ib.reqMktData( contract, snapshot=True, ) + details_fute = self.ib.reqContractDetailsAsync(contract) + details = (await details_fute)[0] + + return contract, ticker, details + + async def get_quote( + self, + symbol: str, + ) -> tuple[Contract, Ticker, ContractDetails]: + ''' + Return a single quote for symbol. + + ''' + contract, ticker, details = await self.get_sym_details(symbol) # ensure a last price gets filled in before we deliver quote for _ in range(25): @@ -514,7 +524,6 @@ class Client: f'Symbol {symbol} is not returning a quote ' 'it may be outside trading hours?') - details = (await details_fute)[0] return contract, ticker, details # async to be consistent for the client proxy, and cuz why not. @@ -935,9 +944,10 @@ async def _trio_run_client_method( **kwargs, ) -> None: - """Asyncio entry point to run tasks against the ``ib_insync`` api. + ''' + Asyncio entry point to run tasks against the ``ib_insync`` api. - """ + ''' ca = tractor.current_actor() assert ca.is_infected_aio() @@ -1357,17 +1367,13 @@ async def stream_quotes( # TODO: support multiple subscriptions sym = symbols[0] - with trio.fail_after(5): + with trio.fail_after(3) as cs: contract, first_ticker, details = await _trio_run_client_method( method='get_quote', symbol=sym, ) - # stream = await start_aio_quote_stream(symbol=sym, contract=contract) - async with open_aio_quote_stream( - symbol=sym, contract=contract - ) as stream: - + def mk_init_msgs() -> dict[str, dict]: # pass back some symbol info like min_tick, trading_hours, etc. syminfo = asdict(details) syminfo.update(syminfo['contract']) @@ -1395,6 +1401,34 @@ async def stream_quotes( 'symbol_info': syminfo, } } + return init_msgs + + if cs.cancelled_caught: + # it might be outside regular trading hours so see if we can at + # least grab history. + + contract, first_ticker, details = await _trio_run_client_method( + method='get_sym_details', + symbol=sym, + ) + + # init_msgs = mk_init_msgs() + + # try again but without timeout and then do feed startup once we + # get one. + contract, first_ticker, details = await _trio_run_client_method( + method='get_quote', + symbol=sym, + ) + + else: + init_msgs = mk_init_msgs() + + + # stream = await start_aio_quote_stream(symbol=sym, contract=contract) + async with open_aio_quote_stream( + symbol=sym, contract=contract + ) as stream: con = first_ticker.contract From fc3baf4bd15a2f65f15ae560f69de357d69a181f Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Fri, 17 Dec 2021 13:35:37 -0500 Subject: [PATCH 6/7] Bump timeout up --- piker/brokers/ib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/piker/brokers/ib.py b/piker/brokers/ib.py index cb4e585a..ed479c38 100644 --- a/piker/brokers/ib.py +++ b/piker/brokers/ib.py @@ -1367,7 +1367,7 @@ async def stream_quotes( # TODO: support multiple subscriptions sym = symbols[0] - with trio.fail_after(3) as cs: + with trio.fail_after(16) as cs: contract, first_ticker, details = await _trio_run_client_method( method='get_quote', symbol=sym, From 746db60e5b7c4849a9db014f3d31fb949b7a07d2 Mon Sep 17 00:00:00 2001 From: Tyler Goodlet Date: Mon, 20 Dec 2021 14:16:38 -0500 Subject: [PATCH 7/7] Increases IB api connect timeout to 1s --- piker/brokers/ib.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/piker/brokers/ib.py b/piker/brokers/ib.py index ed479c38..b378f5f2 100644 --- a/piker/brokers/ib.py +++ b/piker/brokers/ib.py @@ -739,11 +739,13 @@ async def load_aio_clients( client_id: Optional[int] = None, ) -> Client: - '''Return an ``ib_insync.IB`` instance wrapped in our client API. + ''' + Return an ``ib_insync.IB`` instance wrapped in our client API. Client instances are cached for later use. TODO: consider doing this with a ctx mngr eventually? + ''' global _accounts2clients, _client_cache, _scan_ignore @@ -782,7 +784,7 @@ async def load_aio_clients( try_ports = list(ports.values()) ports = try_ports if port is None else [port] # we_connected = [] - connect_timeout = 0.5 if platform.system() != 'Windows' else 1 + connect_timeout = 1 if platform.system() != 'Windows' else 2 combos = list(itertools.product(hosts, ports)) # allocate new and/or reload disconnected but cached clients @@ -1300,6 +1302,10 @@ async def _setup_quote_stream( # resulting in tracebacks spammed to console.. # Manually do the dereg ourselves. teardown() + except trio.WouldBlock: + log.warning(f'channel is blocking symbol feed for {symbol}?' + f'\n{to_trio.statistics}' + ) # except trio.WouldBlock: # # for slow debugging purposes to avoid clobbering prompt