Add `Client.search_symbols()` to all backends, use it in `piker search`

symbol_search
Tyler Goodlet 2021-05-28 12:29:58 -04:00
parent c56c7b8540
commit 7fa9f3f542
7 changed files with 105 additions and 14 deletions

View File

@ -142,8 +142,14 @@ async def maybe_open_runtime(
Start the ``tractor`` runtime (a root actor) if none exists.
"""
settings = _tractor_kwargs
settings.update(kwargs)
if not tractor.current_actor(err_on_no_runtime=False):
async with tractor.open_root_actor(loglevel=loglevel, **kwargs):
async with tractor.open_root_actor(
loglevel=loglevel,
**settings,
):
yield
else:
yield

View File

@ -207,6 +207,25 @@ class Client:
return self._pairs
async def search_symbols(
self,
pattern: str,
limit: int = None,
) -> Dict[str, Any]:
if self._pairs is not None:
data = self._pairs
else:
data = await self.symbol_info()
matches = fuzzy.extractBests(
pattern,
data,
score_cutoff=50,
)
# repack in dict form
return {item[0]['symbol']: item[0]
for item in matches}
async def bars(
self,
symbol: str,

View File

@ -30,7 +30,7 @@ import tractor
from ..cli import cli
from .. import watchlists as wl
from ..log import get_console_log, colorize_json, get_logger
from .._daemon import maybe_spawn_brokerd
from .._daemon import maybe_spawn_brokerd, maybe_open_pikerd
from ..brokers import core, get_brokermod, data
log = get_logger('cli')
@ -273,13 +273,25 @@ def search(config, pattern):
"""Search for symbols from broker backend(s).
"""
# global opts
brokermod = config['brokermods'][0]
brokermods = config['brokermods']
quotes = tractor.run(
partial(core.symbol_search, brokermod, pattern),
start_method='forkserver',
loglevel='info',
# define tractor entrypoint
async def main(func):
async with maybe_open_pikerd(
loglevel=config['loglevel'],
):
return await func()
quotes = trio.run(
main,
partial(
core.symbol_search,
brokermods,
pattern,
),
)
if not quotes:
log.error(f"No matches could be found for {pattern}?")
return

View File

@ -24,8 +24,12 @@ import inspect
from types import ModuleType
from typing import List, Dict, Any, Optional
import trio
from ..log import get_logger
from . import get_brokermod
from .._daemon import maybe_spawn_brokerd
from .api import open_cached_client
log = get_logger(__name__)
@ -126,13 +130,41 @@ async def symbol_info(
return await client.symbol_info(symbol, **kwargs)
async def search_w_brokerd(name: str, pattern: str) -> dict:
async with open_cached_client(name) as client:
# TODO: support multiple asset type concurrent searches.
return await client.search_symbols(pattern=pattern)
async def symbol_search(
brokermod: ModuleType,
brokermods: list[ModuleType],
pattern: str,
**kwargs,
) -> Dict[str, Dict[str, Dict[str, Any]]]:
"""Return symbol info from broker.
"""
async with brokermod.get_client() as client:
# TODO: support multiple asset type concurrent searches.
return await client.search_stocks(pattern=pattern, **kwargs)
results = []
async def search_backend(brokername: str) -> None:
async with maybe_spawn_brokerd(
brokername,
) as portal:
results.append((
brokername,
await portal.run(
search_w_brokerd,
name=brokername,
pattern=pattern,
),
))
async with trio.open_nursery() as n:
for mod in brokermods:
n.start_soon(search_backend, mod.name)
return results

View File

@ -52,7 +52,7 @@ from ..log import get_logger, get_console_log
from .._daemon import maybe_spawn_brokerd
from ..data._source import from_df
from ..data._sharedmem import ShmArray
from ._util import SymbolNotFound
from ._util import SymbolNotFound, NoData
log = get_logger(__name__)
@ -311,6 +311,18 @@ class Client:
else:
return {}
async def search_symbols(
self,
pattern: str,
# how many contracts to search "up to"
upto: int = 3,
asdicts: bool = True,
) -> Dict[str, ContractDetails]:
# TODO add search though our adhoc-locally defined symbol set
# for futes/cmdtys/
return await self.search_stocks(pattern, upto, asdicts)
async def search_futes(
self,
pattern: str,
@ -862,6 +874,13 @@ async def get_bars(
# throttling despite the rps being low
break
elif 'No market data permissions for' in err.message:
# TODO: signalling for no permissions searches
raise NoData(f'Symbol: {sym}')
break
else:
log.exception(
"Data query rate reached: Press `ctrl-alt-f`"
@ -1133,8 +1152,10 @@ async def stream_quotes(
# tell caller quotes are now coming in live
feed_is_live.set()
# last = time.time()
async with aclosing(stream):
async for ticker in stream:
# print(f'ticker rate: {1/(time.time() - last)}')
# print(ticker.vwap)
quote = normalize(
@ -1149,6 +1170,7 @@ async def stream_quotes(
# ugh, clear ticks since we've consumed them
ticker.ticks = []
# last = time.time()
def pack_position(pos: Position) -> Dict[str, Any]:

View File

@ -207,7 +207,7 @@ class Client:
return self._pairs
async def search_stocks(
async def search_symbols(
self,
pattern: str,
limit: int = None,

View File

@ -628,7 +628,7 @@ class Client:
f"Took {time.time() - start} seconds to retreive {len(bars)} bars")
return bars
async def search_stocks(
async def search_symbols(
self,
pattern: str,
# how many contracts to return